|
@@ -33,9 +33,11 @@ import com.fs.company.mapper.CompanyMapper;
|
|
|
import com.fs.company.mapper.CompanyUserMapper;
|
|
import com.fs.company.mapper.CompanyUserMapper;
|
|
|
import com.fs.company.service.ICompanyService;
|
|
import com.fs.company.service.ICompanyService;
|
|
|
import com.fs.config.cloud.CloudHostProper;
|
|
import com.fs.config.cloud.CloudHostProper;
|
|
|
|
|
+import com.fs.core.config.WxOpenProperties;
|
|
|
import com.fs.course.config.CourseConfig;
|
|
import com.fs.course.config.CourseConfig;
|
|
|
import com.fs.course.config.RandomRedPacketConfig;
|
|
import com.fs.course.config.RandomRedPacketConfig;
|
|
|
import com.fs.course.config.RandomRedPacketRule;
|
|
import com.fs.course.config.RandomRedPacketRule;
|
|
|
|
|
+import com.fs.course.constant.CourseConstant;
|
|
|
import com.fs.course.domain.*;
|
|
import com.fs.course.domain.*;
|
|
|
import com.fs.course.dto.CoursePackageDTO;
|
|
import com.fs.course.dto.CoursePackageDTO;
|
|
|
import com.fs.course.mapper.*;
|
|
import com.fs.course.mapper.*;
|
|
@@ -51,6 +53,7 @@ import com.fs.course.service.IFsUserCourseVideoService;
|
|
|
import com.fs.course.vo.*;
|
|
import com.fs.course.vo.*;
|
|
|
import com.fs.course.vo.newfs.*;
|
|
import com.fs.course.vo.newfs.*;
|
|
|
import com.fs.enums.ExceptionCodeEnum;
|
|
import com.fs.enums.ExceptionCodeEnum;
|
|
|
|
|
+import com.fs.feishu.service.FeiShuService;
|
|
|
import com.fs.his.config.AppConfig;
|
|
import com.fs.his.config.AppConfig;
|
|
|
import com.fs.his.domain.FsUser;
|
|
import com.fs.his.domain.FsUser;
|
|
|
import com.fs.his.domain.FsUserIntegralLogs;
|
|
import com.fs.his.domain.FsUserIntegralLogs;
|
|
@@ -102,6 +105,7 @@ import org.springframework.beans.BeanUtils;
|
|
|
import org.springframework.beans.BeansException;
|
|
import org.springframework.beans.BeansException;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
+import org.springframework.context.annotation.Lazy;
|
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
import org.springframework.data.redis.core.RedisTemplate;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
@@ -265,6 +269,29 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
|
|
|
@Value("${cloud_host.company_name}")
|
|
@Value("${cloud_host.company_name}")
|
|
|
private String companyName;
|
|
private String companyName;
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private WxOpenProperties openProperties;
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private FeiShuCourseWatchLogMapper feishuCourseWatchLogMapper;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private FsUserCourseMapper courseMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private FeiShuService feiShuService;
|
|
|
|
|
+
|
|
|
|
|
+ private final BlockingQueue<FsCourseWatchLog> watchLogsQueue = new LinkedBlockingQueue<>(20000);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ private static final String gjminiappLink = "/pages/course/learning?course=";
|
|
|
|
|
+
|
|
|
|
|
+ private static final String feishuMiniappLink = "/pages_course/video?course=";
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 查询课堂视频
|
|
* 查询课堂视频
|
|
|
*
|
|
*
|
|
@@ -1637,13 +1664,15 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
|
|
|
return R.error("未完课");
|
|
return R.error("未完课");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- FsCourseAnswerLogs rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideo(param.getVideoId(), param.getUserId(), param.getQwUserId());
|
|
|
|
|
- if("泽林文化".equals(signProjectName)){
|
|
|
|
|
- rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideoWithPeriodId(param.getVideoId(), param.getUserId(), param.getQwUserId(),param.getPeriodId());
|
|
|
|
|
- }
|
|
|
|
|
- if (rightLog == null) {
|
|
|
|
|
- logger.error("未答题:{}", param.getUserId());
|
|
|
|
|
- return R.error("未答题");
|
|
|
|
|
|
|
+ if(!param.getIsFeiShu()){
|
|
|
|
|
+ FsCourseAnswerLogs rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideo(param.getVideoId(), param.getUserId(), param.getQwUserId());
|
|
|
|
|
+ if("泽林文化".equals(signProjectName)){
|
|
|
|
|
+ rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideoWithPeriodId(param.getVideoId(), param.getUserId(), param.getQwUserId(),param.getPeriodId());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (rightLog == null) {
|
|
|
|
|
+ logger.error("未答题:{}", param.getUserId());
|
|
|
|
|
+ return R.error("未答题");
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (log.getRewardType() != null) {
|
|
if (log.getRewardType() != null) {
|
|
@@ -3258,6 +3287,61 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public R createCartLinkByFeiShu(FsCourseLinkMiniParam param) {
|
|
|
|
|
+
|
|
|
|
|
+ QwUser qwUser = qwExternalContactService.getQwUserByRedis(param.getCorpId().trim(), param.getQwUserId().trim());
|
|
|
|
|
+
|
|
|
|
|
+ if (qwUser==null||qwUser.getCompanyId()==null||qwUser.getCompanyUserId()==null){
|
|
|
|
|
+ return R.error("员工未绑定 销售公司 或 销售 请先绑定");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ QwCompany qwCompany = iQwCompanyService.getQwCompanyByRedis(param.getCorpId());
|
|
|
|
|
+
|
|
|
|
|
+ if (qwCompany == null ) {
|
|
|
|
|
+ return R.error().put("msg","企业不存在,请联系管理员");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String json = configService.selectConfigByKey("course.config");
|
|
|
|
|
+ CourseConfig config = JSON.parseObject(json, CourseConfig.class);
|
|
|
|
|
+
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ return R.error().put("msg","课程默认配置为空,请联系管理员");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Long courseId = param.getCourseId();
|
|
|
|
|
+ //qw_user表的主键id
|
|
|
|
|
+ Long qwUserId = qwUser.getId();
|
|
|
|
|
+ Date createTime = DateUtils.getNowDate();
|
|
|
|
|
+ Long externalUserId = param.getExternalUserId();
|
|
|
|
|
+ Long videoId = param.getVideoId();
|
|
|
|
|
+ Long companyId = qwUser.getCompanyId();
|
|
|
|
|
+ Long companyUserId = qwUser.getCompanyUserId();
|
|
|
|
|
+
|
|
|
|
|
+ addWatchLogIfNeeded(param.getVideoId(), param.getCourseId(),param.getFsUserId(),qwUser , param.getExternalUserId(),3);
|
|
|
|
|
+
|
|
|
|
|
+ //2.创建课程短链
|
|
|
|
|
+ Map<String, String> feishuH5Link = createFeishuH5Link(param.getCorpId(), createTime, courseId.intValue(), videoId.intValue(), String.valueOf(qwUserId), companyUserId, companyId, externalUserId, config);
|
|
|
|
|
+ //获取生成的课程短链码
|
|
|
|
|
+ String shortCode = feishuH5Link.get("link");
|
|
|
|
|
+
|
|
|
|
|
+ //3.调用生成飞书注册授权链接
|
|
|
|
|
+ String feishuLink = feiShuService.getFeishuRegisterLink(videoId, companyId, courseId, companyUserId,shortCode);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ //生成卡片链接
|
|
|
|
|
+ JSONObject news = new JSONObject(true); // true 表示保持字段顺序
|
|
|
|
|
+ news.put("linkTitle", param.getTitle());
|
|
|
|
|
+ news.put("linkDescribe", param.getTitle());
|
|
|
|
|
+ news.put("linkImageUrl", config.getSidebarImageUrl());
|
|
|
|
|
+ news.put("linkUrl", feishuLink);
|
|
|
|
|
+
|
|
|
|
|
+ return R.ok().put("data",news);
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
//插入观看记录
|
|
//插入观看记录
|
|
|
private void addWatchLogIfNeeded(Long videoId, Long courseId,
|
|
private void addWatchLogIfNeeded(Long videoId, Long courseId,
|
|
|
Long fsUserId, QwUser qwUser, Long externalId,Integer watchType) {
|
|
Long fsUserId, QwUser qwUser, Long externalId,Integer watchType) {
|
|
@@ -4660,5 +4744,326 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
|
|
|
|
|
|
|
|
return ResponseResult.ok(vo);
|
|
return ResponseResult.ok(vo);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取飞书课程详情
|
|
|
|
|
+ */
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public R getFeiShuLinkCourseVideoDetails(Long companyUserId, Long videoId, Long identifier) {
|
|
|
|
|
+ CompanyUser companyUser = companyUserMapper.selectCompanyUserById(companyUserId);
|
|
|
|
|
+ //判断该销售是否存在
|
|
|
|
|
+ if (companyUser == null) {
|
|
|
|
|
+ return R.error(405, "当前销售不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取视频详情
|
|
|
|
|
+ FsUserCourseVideoDetailsVO courseVideoDetails = getFeiShuVideoDetails(videoId);
|
|
|
|
|
+
|
|
|
|
|
+ // 查看为最新课程
|
|
|
|
|
+ FsUserCourseAddCompanyUserParam param = new FsUserCourseAddCompanyUserParam();
|
|
|
|
|
+ param.setCourseId(courseVideoDetails.getCourseId());
|
|
|
|
|
+ param.setVideoId(videoId);
|
|
|
|
|
+ param.setCompanyUserId(companyUserId);
|
|
|
|
|
+ Map<String, Object> userCoursePeriodValid = isUserCoursePeriodValid(param);
|
|
|
|
|
+ if (!((boolean)userCoursePeriodValid.get("isWithinRangeSafe"))) {
|
|
|
|
|
+ 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
|
|
|
|
|
+ config.setCourseLogo(fsUserCourse.getImgUrl());
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ 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);
|
|
|
|
|
+ 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);
|
|
|
|
|
+ feishuCourseWatchLogMapper.insert(watchLog);
|
|
|
|
|
+
|
|
|
|
|
+ String heartRedisKey = CourseConstant.getFeiShuWatchHeartKey(identifier, videoId, companyUserId);
|
|
|
|
|
+ redisCache.setCacheObject(heartRedisKey, LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), 5, TimeUnit.MINUTES);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 从Redis中获取用户目前的观看时长
|
|
|
|
|
+ String redisKey = CourseConstant.getFeiShuWatchDurationKey(identifier, videoId, companyUserId);
|
|
|
|
|
+ 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);
|
|
|
|
|
+
|
|
|
|
|
+ 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 identifier, Long duration) {
|
|
|
|
|
+ // 获取视频总时长
|
|
|
|
|
+ Long videoDuration = getAutoLookVideoDuration(videoId);
|
|
|
|
|
+ if (duration > videoDuration + 10) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取用户观看时长
|
|
|
|
|
+ String redisKey = CourseConstant.getFeiShuWatchDurationKey(identifier, videoId, companyUserId);
|
|
|
|
|
+ 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);
|
|
|
|
|
+ redisCache.setCacheObject(heartRedisKey, LocalDateTime.now().toString(), 5, TimeUnit.MINUTES);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 飞书看课流量统计
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void getFeiShuInternetTraffic(Long identifier, Long companyUserId, Long videoId, 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);
|
|
|
|
|
+
|
|
|
|
|
+ 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);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 生成课程链接
|
|
|
|
|
+ * */
|
|
|
|
|
+ private Map<String, String> createFeishuH5Link(String corpId, Date sendTime,
|
|
|
|
|
+ Integer courseId, Integer videoId, String qwUserId,
|
|
|
|
|
+ Long companyUserId, Long companyId, Long externalId, CourseConfig config) {
|
|
|
|
|
+ FsCourseLink link = createFsCourseLink(corpId, sendTime, courseId, videoId, Long.valueOf(qwUserId),
|
|
|
|
|
+ companyUserId, companyId, externalId, 3, null);
|
|
|
|
|
+ FsCourseRealLink courseMap = new FsCourseRealLink();
|
|
|
|
|
+ BeanUtils.copyProperties(link, courseMap);
|
|
|
|
|
+ String courseJson = JSON.toJSONString(courseMap);
|
|
|
|
|
+ String realLinkFull = feishuMiniappLink + courseJson;
|
|
|
|
|
+ if (StringUtils.isNotEmpty(config.getRealLinkGjDomainName())) {
|
|
|
|
|
+ realLinkFull = config.getRealLinkGjDomainName() + realLinkFull;
|
|
|
|
|
+ }
|
|
|
|
|
+ link.setRealLink(realLinkFull);
|
|
|
|
|
+ LocalDate tomorrow = LocalDate.now().plusDays(1);
|
|
|
|
|
+ LocalDateTime expireDateTime = LocalDateTime.of(tomorrow, LocalTime.of(23, 59, 59));
|
|
|
|
|
+ Date expireDate = Date.from(expireDateTime.atZone(ZoneId.systemDefault()).toInstant());
|
|
|
|
|
+
|
|
|
|
|
+ link.setUpdateTime(expireDate);
|
|
|
|
|
+ fsCourseLinkMapper.insertFsCourseLink(link);
|
|
|
|
|
+ Map<String, String> result = new HashMap<>();
|
|
|
|
|
+ result.put("url", link.getRealLink());//真实课程链接
|
|
|
|
|
+ result.put("link", link.getLink());//课程短链
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Map<String, String> createH5GjLinkByMiniApp(String corpId, Date sendTime,
|
|
|
|
|
+ Integer courseId, Integer videoId, String qwUserId,
|
|
|
|
|
+ Long companyUserId, Long companyId, Long externalId, CourseConfig config) {
|
|
|
|
|
+
|
|
|
|
|
+ FsCourseLink link = createFsCourseLink(corpId, sendTime, courseId, videoId, Long.valueOf(qwUserId),
|
|
|
|
|
+ companyUserId, companyId, externalId, 3, null);
|
|
|
|
|
+
|
|
|
|
|
+ FsCourseRealLink courseMap = new FsCourseRealLink();
|
|
|
|
|
+ BeanUtils.copyProperties(link, courseMap);
|
|
|
|
|
+
|
|
|
|
|
+ String courseJson = JSON.toJSONString(courseMap);
|
|
|
|
|
+
|
|
|
|
|
+ String realLinkFull = gjminiappLink + courseJson;
|
|
|
|
|
+ if (StringUtils.isNotEmpty(config.getRealLinkGjDomainName())) {
|
|
|
|
|
+ realLinkFull = config.getRealLinkGjDomainName() + realLinkFull;
|
|
|
|
|
+ }
|
|
|
|
|
+ link.setRealLink(realLinkFull);
|
|
|
|
|
+
|
|
|
|
|
+ //存短链-
|
|
|
|
|
+ fsCourseLinkMapper.insertFsCourseLink(link);
|
|
|
|
|
+ Map<String, String> result = new HashMap<>();
|
|
|
|
|
+ result.put("url", link.getRealLink());
|
|
|
|
|
+ result.put("link", link.getLink());
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public FsCourseLink createFsCourseLink(String corpId, Date sendTime, Integer courseId, Integer videoId, Long qwUserId,
|
|
|
|
|
+ Long companyUserId, Long companyId, Long externalId, Integer type, String chatId) {
|
|
|
|
|
+ // 手动创建 FsCourseLink 对象,避免使用 BeanUtils.copyProperties
|
|
|
|
|
+ FsCourseLink link = new FsCourseLink();
|
|
|
|
|
+ link.setCompanyId(companyId);
|
|
|
|
|
+ link.setQwUserId(qwUserId);
|
|
|
|
|
+ link.setCompanyUserId(companyUserId);
|
|
|
|
|
+ link.setVideoId(videoId.longValue());
|
|
|
|
|
+ link.setCorpId(corpId);
|
|
|
|
|
+ link.setCourseId(courseId.longValue());
|
|
|
|
|
+ link.setChatId(chatId);
|
|
|
|
|
+ link.setQwExternalId(externalId);
|
|
|
|
|
+ link.setLinkType(type); //小程序
|
|
|
|
|
+ link.setIsRoom(1);
|
|
|
|
|
+ link.setUNo(UUID.randomUUID().toString());
|
|
|
|
|
+ String randomString = generateRandomStringWithLock();
|
|
|
|
|
+ if (StringUtil.strIsNullOrEmpty(randomString)) {
|
|
|
|
|
+ link.setLink(UUID.randomUUID().toString().replace("-", ""));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ link.setLink(randomString);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ link.setCreateTime(sendTime);
|
|
|
|
|
+ link.setProjectCode(cloudHostProper.getProjectCode());
|
|
|
|
|
+
|
|
|
|
|
+ return link;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void addWatchLogIfNeeded(Long fsUserId, Long videoId, Long courseId,
|
|
|
|
|
+ Date sendTime, String qwUserId, Long companyUserId,
|
|
|
|
|
+ Long companyId, Long externalId) {
|
|
|
|
|
+ FsCourseWatchLog watchLog = new FsCourseWatchLog();
|
|
|
|
|
+ watchLog.setVideoId(videoId);
|
|
|
|
|
+ watchLog.setQwExternalContactId(externalId);
|
|
|
|
|
+ watchLog.setSendType(2);
|
|
|
|
|
+ watchLog.setQwUserId(Long.parseLong(qwUserId));
|
|
|
|
|
+ watchLog.setDuration(0L);
|
|
|
|
|
+ watchLog.setCourseId(courseId);
|
|
|
|
|
+ watchLog.setCompanyUserId(companyUserId);
|
|
|
|
|
+ watchLog.setCompanyId(companyId);
|
|
|
|
|
+ watchLog.setCreateTime(sendTime);
|
|
|
|
|
+ watchLog.setUpdateTime(new Date());
|
|
|
|
|
+ watchLog.setLogType(3);
|
|
|
|
|
+ watchLog.setUserId(fsUserId);
|
|
|
|
|
+ enqueueWatchLog(watchLog);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 将 FsCourseWatchLog 放入队列
|
|
|
|
|
+ */
|
|
|
|
|
+ private void enqueueWatchLog(FsCourseWatchLog watchLog) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ boolean offered = watchLogsQueue.offer(watchLog, 5, TimeUnit.SECONDS);
|
|
|
|
|
+ if (!offered) {
|
|
|
|
|
+ log.error("FsCourseWatchLog 队列已满,无法添加日志: {}", JSON.toJSONString(watchLog));
|
|
|
|
|
+ // 处理队列已满的情况,例如记录到失败队列或持久化存储
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (InterruptedException e) {
|
|
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
|
|
+ log.error("插入 FsCourseWatchLog 队列时被中断: {}", e.getMessage(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
|
|
|