|
|
@@ -3,23 +3,17 @@ package com.fs.course.service.impl;
|
|
|
import cn.hutool.json.JSONUtil;
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
-import com.baomidou.mybatisplus.core.conditions.Wrapper;
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
|
|
|
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
import com.fs.common.BeanCopyUtils;
|
|
|
-import com.fs.common.constant.FsConstants;
|
|
|
import com.fs.common.core.domain.R;
|
|
|
import com.fs.common.core.domain.ResponseResult;
|
|
|
import com.fs.common.core.domain.entity.SysDictData;
|
|
|
import com.fs.common.core.redis.RedisCache;
|
|
|
import com.fs.common.enums.BizResponseEnum;
|
|
|
-import com.fs.common.exception.CustomException;
|
|
|
import com.fs.common.exception.ServiceException;
|
|
|
-import com.fs.common.exception.base.BaseException;
|
|
|
import com.fs.common.utils.CloudHostUtils;
|
|
|
import com.fs.common.utils.DateUtils;
|
|
|
-import com.fs.common.utils.PubFun;
|
|
|
import com.fs.common.utils.StringUtils;
|
|
|
import com.fs.common.utils.date.DateUtil;
|
|
|
import com.fs.company.constant.CompanyTrafficConstants;
|
|
|
@@ -33,6 +27,7 @@ import com.fs.company.util.CompanyRedPacketBalanceUtil;
|
|
|
import com.fs.config.cloud.CloudHostProper;
|
|
|
import com.fs.core.utils.OrderCodeUtils;
|
|
|
import com.fs.course.config.CourseConfig;
|
|
|
+import com.fs.course.constant.CourseConstant;
|
|
|
import com.fs.course.domain.*;
|
|
|
import com.fs.course.dto.CoursePackageDTO;
|
|
|
import com.fs.course.mapper.*;
|
|
|
@@ -41,7 +36,6 @@ import com.fs.course.param.newfs.*;
|
|
|
import com.fs.course.service.IFsUserCompanyBindService;
|
|
|
import com.fs.course.service.IFsUserCompanyUserService;
|
|
|
import com.fs.course.service.IFsUserCourseVideoService;
|
|
|
-import com.fs.course.service.IFsVideoResourceService;
|
|
|
import com.fs.course.vo.*;
|
|
|
import com.fs.course.vo.newfs.*;
|
|
|
import com.fs.his.config.AppConfig;
|
|
|
@@ -67,7 +61,6 @@ import com.fs.qwApi.Result.QwAddContactWayResult;
|
|
|
import com.fs.qwApi.param.QwAddContactWayParam;
|
|
|
import com.fs.qwApi.service.QwApiService;
|
|
|
import com.fs.sop.domain.SopUserLogsInfo;
|
|
|
-import com.fs.sop.mapper.QwSopLogsMapper;
|
|
|
import com.fs.sop.mapper.SopUserLogsInfoMapper;
|
|
|
import com.fs.sop.service.ISopUserLogsInfoService;
|
|
|
import com.fs.system.mapper.SysDictDataMapper;
|
|
|
@@ -160,6 +153,8 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
|
|
|
@Autowired
|
|
|
private FsCourseWatchLogMapper courseWatchLogMapper;
|
|
|
@Autowired
|
|
|
+ private FeiShuCourseWatchLogMapper feishuCourseWatchLogMapper;
|
|
|
+ @Autowired
|
|
|
private ISopUserLogsInfoService iSopUserLogsInfoService;
|
|
|
@Autowired
|
|
|
private FsCourseLinkMapper fsCourseLinkMapper;
|
|
|
@@ -3854,5 +3849,249 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取飞书课程详情
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public R getFeiShuLinkCourseVideoDetails(Long companyUserId, Long videoId, Long periodId, Long identifier) {
|
|
|
+ CompanyUser companyUser = companyUserMapper.selectCompanyUserById(companyUserId);
|
|
|
+ //判断该销售是否存在
|
|
|
+ if (companyUser == null) {
|
|
|
+ return R.error(405, "当前销售不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取视频详情
|
|
|
+ FsUserCourseVideoDetailsVO courseVideoDetails = getFeiShuVideoDetails(videoId);
|
|
|
+
|
|
|
+ // 查看为最新课程
|
|
|
+ FsUserCourseAddCompanyUserParam param = new FsUserCourseAddCompanyUserParam();
|
|
|
+ param.setPeriodId(periodId);
|
|
|
+ param.setCourseId(courseVideoDetails.getCourseId());
|
|
|
+ param.setVideoId(videoId);
|
|
|
+ param.setCompanyUserId(companyUserId);
|
|
|
+ if (!isUserCoursePeriodValid(param)) {
|
|
|
+ return R.error(504, "请观看最新的课程项目");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取课程所属项目id
|
|
|
+ FsUserCourse fsUserCourse = fsUserCourseMapper.selectFsUserCourseByCourseId(param.getCourseId());
|
|
|
+ Long courseProject = fsUserCourse.getProject();
|
|
|
+ if (Objects.isNull(courseProject)) {
|
|
|
+ return R.error(504, "课程配置错误,项目归属为空,课程ID: " + param.getCourseId());
|
|
|
+ }
|
|
|
+
|
|
|
+ String json = configService.selectConfigByKey("course.config");
|
|
|
+ CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
|
|
|
+
|
|
|
+ // 课程logo
|
|
|
+ FsUserCoursePeriod fsUserCoursePeriod = fsUserCoursePeriodMapper.selectFsUserCoursePeriodById(periodId);
|
|
|
+ if (fsUserCoursePeriod != null) {
|
|
|
+ config.setCourseLogo(fsUserCoursePeriod.getCourseLogo());
|
|
|
+ }
|
|
|
+
|
|
|
+ long duration = 0L;
|
|
|
+ long tipsTime = 0L;
|
|
|
+ long tipsTime2 = 0L;
|
|
|
+ int isFinish = 0;
|
|
|
+ FsUserCourseVideoLinkDetailsVO vo = new FsUserCourseVideoLinkDetailsVO();
|
|
|
+ vo.setCourseVideoDetails(courseVideoDetails);
|
|
|
+ vo.setCourseConfig(config);
|
|
|
+ vo.setIsFinish(isFinish);
|
|
|
+ vo.setPlayDuration(duration);
|
|
|
+
|
|
|
+ // 获取看课记录
|
|
|
+ FeiShuCourseWatchLog watchLog = feishuCourseWatchLogMapper.getWatchLogByFsUser(videoId, identifier, companyUserId, periodId);
|
|
|
+ if (watchLog == null) {
|
|
|
+ watchLog = new FeiShuCourseWatchLog();
|
|
|
+ watchLog.setUserId(identifier);
|
|
|
+ watchLog.setCourseId(courseVideoDetails.getCourseId());
|
|
|
+ watchLog.setVideoId(videoId);
|
|
|
+ watchLog.setCompanyId(companyUser.getCompanyId());
|
|
|
+ watchLog.setCompanyUserId(companyUserId);
|
|
|
+ watchLog.setSendType(1);
|
|
|
+ watchLog.setDuration(0);
|
|
|
+ watchLog.setCreateTime(LocalDateTime.now());
|
|
|
+ watchLog.setLogType(1);
|
|
|
+ watchLog.setProject(courseProject);
|
|
|
+ watchLog.setPeriodId(courseProject);
|
|
|
+ watchLog.setPeriodId(periodId);
|
|
|
+ feishuCourseWatchLogMapper.insert(watchLog);
|
|
|
+
|
|
|
+ String heartRedisKey = CourseConstant.getFeiShuWatchHeartKey(identifier, videoId, companyUserId, periodId);
|
|
|
+ redisCache.setCacheObject(heartRedisKey, LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), 5, TimeUnit.MINUTES);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 从Redis中获取用户目前的观看时长
|
|
|
+ String redisKey = CourseConstant.getFeiShuWatchDurationKey(identifier, videoId, companyUserId, periodId);
|
|
|
+ Long durationCurrent = redisCache.getCacheObject(redisKey);
|
|
|
+ if (durationCurrent != null) {
|
|
|
+ duration = durationCurrent;
|
|
|
+ } else {
|
|
|
+ duration = watchLog.getDuration();
|
|
|
+ redisCache.setCacheObject(redisKey, duration, 2, TimeUnit.HOURS);
|
|
|
+ }
|
|
|
+
|
|
|
+ //判断是否完课
|
|
|
+ if (watchLog.getLogType() == 2) {
|
|
|
+ isFinish = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ vo.setTipsTime(tipsTime);
|
|
|
+ vo.setTipsTime2(tipsTime2);
|
|
|
+ vo.setIsFinish(isFinish);
|
|
|
+ vo.setPlayDuration(duration);
|
|
|
+
|
|
|
+ //判断营期的课程状态是否是进行中
|
|
|
+ FsUserCoursePeriodDays days = fsUserCoursePeriodDaysMapper.selectByPeriodAndVideoId(periodId, videoId);
|
|
|
+
|
|
|
+ // 查询销售设置的看课时间
|
|
|
+ LocalDateTime companyUserStartDateTime = null;
|
|
|
+ LocalDateTime companyUserEndDateTime = null;
|
|
|
+
|
|
|
+ List<CompanyUserTimeQueryParam> queryList = new ArrayList<>();
|
|
|
+ CompanyUserTimeQueryParam query = new CompanyUserTimeQueryParam();
|
|
|
+ query.setPeriodId(periodId);
|
|
|
+ query.setCourseId(courseVideoDetails.getCourseId());
|
|
|
+ query.setVideoId(videoId);
|
|
|
+ query.setCompanyUserId(companyUserId);
|
|
|
+ queryList.add(query);
|
|
|
+ List<FsUserCourseCompanyUserTime> fsUserCourseCompanyUserTimes = companyUserTimeMapper.batchSelectByParams(queryList);
|
|
|
+
|
|
|
+ if (CollectionUtils.isNotEmpty(fsUserCourseCompanyUserTimes)) {
|
|
|
+ FsUserCourseCompanyUserTime fsUserCourseCompanyUserTime = fsUserCourseCompanyUserTimes.get(0);
|
|
|
+ Date cuStartDateTime = fsUserCourseCompanyUserTime.getStartDateTime();
|
|
|
+ Date cuEndDateTime = fsUserCourseCompanyUserTime.getEndDateTime();
|
|
|
+
|
|
|
+ if (cuStartDateTime != null) {
|
|
|
+ Instant instant = cuStartDateTime.toInstant();
|
|
|
+ ZoneId zoneId = ZoneId.systemDefault();
|
|
|
+ companyUserStartDateTime = instant.atZone(zoneId).toLocalDateTime();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (cuEndDateTime != null) {
|
|
|
+ Instant instant = cuEndDateTime.toInstant();
|
|
|
+ ZoneId zoneId = ZoneId.systemDefault();
|
|
|
+ companyUserEndDateTime = instant.atZone(zoneId).toLocalDateTime();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ vo.setStartDateTime(companyUserStartDateTime != null ? companyUserStartDateTime : days.getStartDateTime());
|
|
|
+ vo.setEndDateTime(companyUserEndDateTime != null ? companyUserEndDateTime : days.getEndDateTime());
|
|
|
+ vo.setRang(DateUtil.isWithinRangeSafe(LocalDateTime.now(),
|
|
|
+ companyUserStartDateTime != null ? companyUserStartDateTime : days.getStartDateTime(),
|
|
|
+ companyUserEndDateTime != null ? companyUserEndDateTime : days.getEndDateTime())
|
|
|
+ && days.getStatus() == 1);
|
|
|
+
|
|
|
+ return R.ok().put("data", vo);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取视频详情
|
|
|
+ */
|
|
|
+ private FsUserCourseVideoDetailsVO getFeiShuVideoDetails(Long videoId) {
|
|
|
+ FsUserCourseVideo fsUserCourseVideo = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId(videoId);
|
|
|
+ FsUserCourseVideoDetailsVO fsUserCourseVideoDetailsVO = new FsUserCourseVideoDetailsVO();
|
|
|
+ BeanUtils.copyProperties(fsUserCourseVideo, fsUserCourseVideoDetailsVO);
|
|
|
+
|
|
|
+ //这里 改成取线路一值,返回给前端。VideoUrl 是原视频(用来算流量的),不要去改,lineOne是转码后的视频
|
|
|
+ fsUserCourseVideoDetailsVO.setVideoUrl(fsUserCourseVideo.getLineOne());
|
|
|
+
|
|
|
+ // 获取课程相关的题库
|
|
|
+ String questionBankId = fsUserCourseVideo.getQuestionBankId();
|
|
|
+ List<FsUserVideoQuestionVO> questionList = Collections.emptyList();
|
|
|
+ if (StringUtils.isNotEmpty(questionBankId)) {
|
|
|
+ String[] questionBankIds = questionBankId.split(",");
|
|
|
+ List<FsCourseQuestionBank> fsCourseQuestionBanks = courseQuestionBankMapper.selectFsCourseQuestionBankByIdVO(questionBankIds);
|
|
|
+ questionList = fsCourseQuestionBanks.stream().map(v -> {
|
|
|
+ FsUserVideoQuestionVO fsUserVideoQuestionVO = new FsUserVideoQuestionVO();
|
|
|
+ BeanUtils.copyProperties(v, fsUserVideoQuestionVO);
|
|
|
+ return fsUserVideoQuestionVO;
|
|
|
+ }).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ fsUserCourseVideoDetailsVO.setQuestionBankList(questionList);
|
|
|
+ return fsUserCourseVideoDetailsVO;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新飞书看课时长
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void feiShuUpdateWatchDurationWx(Long companyUserId, Long videoId, Long periodId, Long identifier, Long duration) {
|
|
|
+ // 获取视频总时长
|
|
|
+ Long videoDuration = getAutoLookVideoDuration(videoId);
|
|
|
+ if (duration > videoDuration + 10) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取用户观看时长
|
|
|
+ String redisKey = CourseConstant.getFeiShuWatchDurationKey(identifier, videoId, companyUserId, periodId);
|
|
|
+ Long userLookDuration = redisCache.getCacheObject(redisKey);
|
|
|
+
|
|
|
+ // 更新观看时长
|
|
|
+ if (duration > (userLookDuration != null ? userLookDuration : 0) + 90) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ redisCache.setCacheObject(redisKey, duration, 2, TimeUnit.HOURS);
|
|
|
+
|
|
|
+ // 更新心跳
|
|
|
+ String heartRedisKey = CourseConstant.getFeiShuWatchHeartKey(identifier, videoId, companyUserId, periodId);
|
|
|
+ redisCache.setCacheObject(heartRedisKey, LocalDateTime.now().toString(), 5, TimeUnit.MINUTES);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 飞书看课流量统计
|
|
|
+ */
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ @Override
|
|
|
+ public void getFeiShuInternetTraffic(Long identifier, Long companyUserId, Long videoId, Long periodId, String uuid, BigDecimal bufferRate) {
|
|
|
+ if (StringUtils.isBlank(uuid)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ FsCourseTrafficLog trafficLog = new FsCourseTrafficLog();
|
|
|
+ trafficLog.setCreateTime(new Date());
|
|
|
+ trafficLog.setTypeFlag(2);
|
|
|
+ trafficLog.setUuId(uuid);
|
|
|
+ trafficLog.setUserId(identifier);
|
|
|
+ trafficLog.setVideoId(videoId);
|
|
|
+ trafficLog.setCompanyUserId(companyUserId);
|
|
|
+ trafficLog.setPeriodId(periodId);
|
|
|
+
|
|
|
+ FsUserCourseVideo video = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId(videoId);
|
|
|
+ if (video == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ CompanyUser companyUser = companyUserMapper.selectCompanyUserById(companyUserId);
|
|
|
+ if (companyUser == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ Company company = companyMapper.selectCompanyById(companyUser.getCompanyId());
|
|
|
+ if (company == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ trafficLog.setCompanyId(company.getCompanyId());
|
|
|
+ trafficLog.setCourseId(video.getCourseId());
|
|
|
+
|
|
|
+
|
|
|
+ // 计算流量
|
|
|
+ BigDecimal result = bufferRate.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);
|
|
|
+
|
|
|
+ // 获取课程所属项目id
|
|
|
+ FsUserCourse fsUserCourse = fsUserCourseMapper.selectFsUserCourseByCourseId(video.getCourseId());
|
|
|
+ if (fsUserCourse != null) {
|
|
|
+ trafficLog.setProject(fsUserCourse.getProject());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 插入或更新
|
|
|
+ fsCourseTrafficLogMapper.insertOrUpdateTrafficLog(trafficLog);
|
|
|
+ asyncDeductTraffic(company, trafficLog);
|
|
|
+ }
|
|
|
}
|
|
|
|