|
|
@@ -31,6 +31,7 @@ import com.fs.company.mapper.CompanyUserMapper;
|
|
|
import com.fs.company.service.ICompanyService;
|
|
|
import com.fs.config.cloud.CloudHostProper;
|
|
|
import com.fs.course.config.CourseConfig;
|
|
|
+import com.fs.course.constant.CourseConstant;
|
|
|
import com.fs.course.domain.*;
|
|
|
import com.fs.course.dto.CoursePeriodSyncResultDTO;
|
|
|
import com.fs.course.dto.CoursePackageDTO;
|
|
|
@@ -50,6 +51,7 @@ import com.fs.course.param.newfs.*;
|
|
|
import com.fs.course.service.*;
|
|
|
import com.fs.course.vo.*;
|
|
|
import com.fs.course.vo.newfs.*;
|
|
|
+import com.fs.feishu.service.FeiShuService;
|
|
|
import com.fs.enums.ExceptionCodeEnum;
|
|
|
import com.fs.his.config.AppConfig;
|
|
|
import com.fs.his.domain.FsUser;
|
|
|
@@ -147,6 +149,7 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
|
|
|
private static final Logger logger = LoggerFactory.getLogger(FsUserCourseVideoServiceImpl.class);
|
|
|
|
|
|
private static final String miniappRealLink = "/pages_course/video.html?course=";
|
|
|
+ private static final String feishuMiniappLink = "/pages_course/video?course=";
|
|
|
private static final String REAL_LINK_PREFIX = "/courseH5/pages/course/learning?course=";
|
|
|
private static final String SHORT_LINK_PREFIX = "/courseH5/pages/course/learning?s=";
|
|
|
// 排除看课数量限制的公司集合
|
|
|
@@ -176,6 +179,8 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
|
|
|
@Autowired
|
|
|
private FsCourseWatchLogMapper courseWatchLogMapper;
|
|
|
@Autowired
|
|
|
+ private FeiShuCourseWatchLogMapper feishuCourseWatchLogMapper;
|
|
|
+ @Autowired
|
|
|
private IFsCourseWatchLogService courseWatchLogService;
|
|
|
@Autowired
|
|
|
private ISopUserLogsInfoService iSopUserLogsInfoService;
|
|
|
@@ -284,6 +289,9 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
|
|
|
@Autowired
|
|
|
private RedisTemplate redisTemplate;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private FeiShuService feiShuService;
|
|
|
+
|
|
|
|
|
|
@Autowired
|
|
|
private IFsCourseLinkService linkService;
|
|
|
@@ -955,22 +963,17 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
|
|
|
if (qwGroupChatUsers == null || qwGroupChatUsers.isEmpty()) {
|
|
|
return R.error(ExceptionCodeEnum.GROUP_PARAM_ERROR.getCode(), ExceptionCodeEnum.GROUP_PARAM_ERROR.getDescription());
|
|
|
}
|
|
|
- QwExternalContact qwExternalContact = qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>()
|
|
|
- .eq("user_id", qwGroupChat.getOwner())
|
|
|
- .eq("fs_user_id", param.getUserId())
|
|
|
- .eq("corp_id", param.getCorpId()));
|
|
|
+ QwExternalContact qwExternalContact = selectValidQwExternalContact(new QueryWrapper<QwExternalContact>()
|
|
|
+ .eq("user_id", qwGroupChat.getOwner())
|
|
|
+ .eq("fs_user_id", param.getUserId())
|
|
|
+ .eq("corp_id", param.getCorpId()));
|
|
|
|
|
|
- if(null == qwExternalContact){
|
|
|
- try{
|
|
|
- //修改成通过昵称匹配
|
|
|
- qwExternalContact =
|
|
|
- qwExternalContactMapper.selectOne(new QueryWrapper<QwExternalContact>()
|
|
|
- .eq("user_id", qwGroupChat.getOwner())
|
|
|
- .eq("name", user.getNickName())
|
|
|
- .eq("corp_id", param.getCorpId()));
|
|
|
- } catch(Exception e){
|
|
|
- log.error("群聊用户昵称匹配异常,参数user_id:{},name:{},corp_id:{}",qwGroupChat.getOwner(),user.getNickName(),param.getCorpId(),e);
|
|
|
- }
|
|
|
+ if (qwExternalContact == null) {
|
|
|
+ // 通过昵称匹配
|
|
|
+ qwExternalContact = selectValidQwExternalContact(new QueryWrapper<QwExternalContact>()
|
|
|
+ .eq("user_id", qwGroupChat.getOwner())
|
|
|
+ .eq("name", user.getNickName())
|
|
|
+ .eq("corp_id", param.getCorpId()));
|
|
|
}
|
|
|
if(qwExternalContact==null){
|
|
|
return R.error(noRegisterMsg);
|
|
|
@@ -1044,6 +1047,18 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
|
|
|
}
|
|
|
return R.error(567, "群聊通用链接").put("qwExternalId", qwExternalContact.getId());
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询有效状态的企微外部联系人(0正常 1待接替 2接替中),排除删除/流失;多条时优先正常状态、最新记录
|
|
|
+ */
|
|
|
+ private QwExternalContact selectValidQwExternalContact(QueryWrapper<QwExternalContact> queryWrapper) {
|
|
|
+ List<QwExternalContact> list = qwExternalContactMapper.selectList(
|
|
|
+ queryWrapper.in("status", Arrays.asList(0, 1, 2))
|
|
|
+ .orderByAsc("status")
|
|
|
+ .orderByDesc("create_time"));
|
|
|
+ return CollectionUtils.isEmpty(list) ? null : list.get(0);
|
|
|
+ }
|
|
|
+
|
|
|
private R handleRoom(FsUserCourseVideoAddKfUParam param,FsUser user,String noRegisterMsg) {
|
|
|
//查询客户列表
|
|
|
List<QwExternalContact> contacts = qwExternalContactMapper.selectQwExternalContactListVOByfsUserId(user.getUserId());
|
|
|
@@ -3560,6 +3575,53 @@ 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();
|
|
|
+ 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(), 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");
|
|
|
+
|
|
|
+ String feishuLink = feiShuService.getFeishuRegisterLink(videoId, companyId, courseId, companyUserId, shortCode);
|
|
|
+
|
|
|
+ JSONObject news = new JSONObject(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,
|
|
|
Long fsUserId, QwUser qwUser, Long externalId,Integer watchType) {
|
|
|
@@ -5316,6 +5378,221 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
|
|
|
return Objects.equals(user.getRedStatus(), 1);
|
|
|
}
|
|
|
|
|
|
+ @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);
|
|
|
+ if (!isUserCoursePeriodValid(param)) {
|
|
|
+ return R.error(504, "请观看最新的课程项目");
|
|
|
+ }
|
|
|
+
|
|
|
+ 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);
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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);
|
|
|
+ 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);
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ public FsCourseLink createFsCourseLink(String corpId, Date sendTime, Integer courseId, Integer videoId, Long qwUserId,
|
|
|
+ Long companyUserId, Long companyId, Long externalId, Integer type, String chatId) {
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
}
|
|
|
|