|
|
@@ -11,6 +11,9 @@ import com.fs.hisStore.domain.FsUserScrm;
|
|
|
import com.fs.hisStore.service.IFsUserScrmService;
|
|
|
import com.fs.live.config.ProductionWordFilter;
|
|
|
import com.fs.live.mapper.LiveCouponMapper;
|
|
|
+import com.fs.live.vo.LiveWatchUserEntry;
|
|
|
+import com.fs.live.domain.LiveWatchLog;
|
|
|
+import com.fs.live.domain.LiveVideo;
|
|
|
import com.fs.live.websocket.auth.WebSocketConfigurator;
|
|
|
import com.fs.live.websocket.bean.SendMsgVo;
|
|
|
import com.fs.common.core.domain.R;
|
|
|
@@ -55,7 +58,7 @@ public class WebSocketServer {
|
|
|
// 心跳超时缓存:key=sessionId,value=最后心跳时间戳
|
|
|
private final static ConcurrentHashMap<String, Long> heartbeatCache = new ConcurrentHashMap<>();
|
|
|
// 心跳超时时间(毫秒):3分钟无心跳则认为超时
|
|
|
- private final static long HEARTBEAT_TIMEOUT = 3 * 60 * 1000;
|
|
|
+ private final static long HEARTBEAT_TIMEOUT = 1 * 60 * 1000;
|
|
|
// admin房间消息发送线程池(单线程,保证串行化)
|
|
|
private final static ConcurrentHashMap<Long, ExecutorService> adminExecutors = new ConcurrentHashMap<>();
|
|
|
|
|
|
@@ -72,7 +75,12 @@ public class WebSocketServer {
|
|
|
private final ILiveUserFirstEntryService liveUserFirstEntryService = SpringUtils.getBean(ILiveUserFirstEntryService.class);
|
|
|
private final ILiveCouponIssueService liveCouponIssueService = SpringUtils.getBean(ILiveCouponIssueService.class);
|
|
|
private final LiveCouponMapper liveCouponMapper = SpringUtils.getBean(LiveCouponMapper.class);
|
|
|
+ private final ILiveWatchLogService liveWatchLogService = SpringUtils.getBean(ILiveWatchLogService.class);
|
|
|
+ private final ILiveVideoService liveVideoService = SpringUtils.getBean(ILiveVideoService.class);
|
|
|
private static Random random = new Random();
|
|
|
+
|
|
|
+ // Redis key 前缀:用户进入直播间时间
|
|
|
+ private static final String USER_ENTRY_TIME_KEY = "live:user:entry:time:%s:%s"; // liveId:userId
|
|
|
|
|
|
// 直播间在线用户缓存
|
|
|
// private static final ConcurrentHashMap<Long, Integer> liveOnlineUsers = new ConcurrentHashMap<>();
|
|
|
@@ -114,6 +122,11 @@ public class WebSocketServer {
|
|
|
|
|
|
LiveWatchUser liveWatchUserVO = liveWatchUserService.join(fsUser,liveId, userId, location);
|
|
|
room.put(userId, session);
|
|
|
+
|
|
|
+ // 存储用户进入直播间的时间到 Redis(用于计算在线时长)
|
|
|
+ String entryTimeKey = String.format(USER_ENTRY_TIME_KEY, liveId, userId);
|
|
|
+ redisCache.setCacheObject(entryTimeKey, System.currentTimeMillis(), 24, TimeUnit.HOURS);
|
|
|
+
|
|
|
// 直播间浏览量 +1
|
|
|
redisCache.incr(PAGE_VIEWS_KEY + liveId, 1);
|
|
|
|
|
|
@@ -161,6 +174,18 @@ public class WebSocketServer {
|
|
|
}
|
|
|
|
|
|
LiveUserFirstEntry liveUserFirstEntry = liveUserFirstEntryService.selectEntityByLiveIdUserId(liveId, userId);
|
|
|
+ // 如果用户连上了 socket,并且公司ID和销售ID大于0,更新 LiveWatchLog 的 logType
|
|
|
+
|
|
|
+ if ((companyId > 0 && companyUserId > 0) || (liveUserFirstEntry != null && liveUserFirstEntry.getCompanyId() > 0 && liveUserFirstEntry.getCompanyUserId() > 0 )) {
|
|
|
+ // 获取当前直播/回放状态
|
|
|
+ Map<String, Integer> flagMap = liveWatchUserService.getLiveFlagWithCache(liveId);
|
|
|
+ Integer currentLiveFlag = flagMap.get("liveFlag");
|
|
|
+
|
|
|
+ // 如果当前是直播状态(liveFlag = 1),更新 logType
|
|
|
+ if (currentLiveFlag != null && currentLiveFlag == 1) {
|
|
|
+ updateLiveWatchLogTypeOnConnect(liveId, userId, companyId, companyUserId);
|
|
|
+ }
|
|
|
+ }
|
|
|
if (liveUserFirstEntry != null) {
|
|
|
// 处理第一次自己进入,第二次扫码销售进入
|
|
|
if (liveUserFirstEntry.getCompanyUserId() == -1L && companyUserId != -1L) {
|
|
|
@@ -205,6 +230,15 @@ public class WebSocketServer {
|
|
|
@OnClose
|
|
|
public void onClose(Session session) {
|
|
|
Map<String, Object> userProperties = session.getUserProperties();
|
|
|
+ // 获取公司ID和销售ID
|
|
|
+ long companyId = -1L;
|
|
|
+ long companyUserId = -1L;
|
|
|
+ if (!Objects.isNull(userProperties.get("companyId"))) {
|
|
|
+ companyId = (long) userProperties.get("companyId");
|
|
|
+ }
|
|
|
+ if (!Objects.isNull(userProperties.get("companyUserId"))) {
|
|
|
+ companyUserId = (long) userProperties.get("companyUserId");
|
|
|
+ }
|
|
|
|
|
|
long liveId = (long) userProperties.get("liveId");
|
|
|
long userId = (long) userProperties.get("userId");
|
|
|
@@ -217,6 +251,8 @@ public class WebSocketServer {
|
|
|
if (Objects.isNull(fsUser)) {
|
|
|
throw new BaseException("用户信息错误");
|
|
|
}
|
|
|
+ // 计算并更新用户在线时长
|
|
|
+ updateUserOnlineDuration(liveId, userId, companyId, companyUserId);
|
|
|
room.remove(userId);
|
|
|
if (room.isEmpty()) {
|
|
|
rooms.remove(liveId);
|
|
|
@@ -228,6 +264,7 @@ public class WebSocketServer {
|
|
|
// 从在线用户Set中移除用户ID
|
|
|
String onlineUsersSetKey = ONLINE_USERS_SET_KEY + liveId;
|
|
|
redisCache.redisTemplate.opsForSet().remove(onlineUsersSetKey, String.valueOf(userId));
|
|
|
+
|
|
|
LiveWatchUser liveWatchUserVO = liveWatchUserService.close(fsUser,liveId, userId);
|
|
|
|
|
|
|
|
|
@@ -565,7 +602,12 @@ public class WebSocketServer {
|
|
|
sendMsgVo.setData(String.valueOf(scoreAmount));
|
|
|
|
|
|
if(Objects.isNull( session)) return;
|
|
|
- session.getAsyncRemote().sendText(JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
|
|
|
+ // 使用带锁的sendMessage方法,保证线程安全
|
|
|
+ try {
|
|
|
+ sendMessage(session, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("发送积分消息失败: liveId={}, userId={}, error={}", liveId, userId, e.getMessage(), e);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private void sendBlockMessage(Long liveId, Long userId) {
|
|
|
@@ -585,40 +627,56 @@ public class WebSocketServer {
|
|
|
sendMsgVo.setData(null);
|
|
|
|
|
|
if(Objects.isNull( session)) return;
|
|
|
- session.getAsyncRemote().sendText(JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
|
|
|
+ // 使用带锁的sendMessage方法,保证线程安全
|
|
|
+ try {
|
|
|
+ sendMessage(session, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
|
|
|
+ } catch (IOException e) {
|
|
|
+ log.error("发送封禁消息失败: liveId={}, userId={}, error={}", liveId, userId, e.getMessage(), e);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 广播消息
|
|
|
* @param liveId 直播间ID
|
|
|
* @param message 消息内容
|
|
|
+ * 优化:使用快照遍历,避免在遍历过程中修改集合
|
|
|
*/
|
|
|
public void broadcastWebMessage(Long liveId, String message) {
|
|
|
ConcurrentHashMap<Long, Session> room = getRoom(liveId);
|
|
|
+
|
|
|
+ if (room.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- // 普通用户房间:并行发送
|
|
|
- room.forEach((k, v) -> {
|
|
|
- if (v.isOpen()) {
|
|
|
- sendWithRetry(v,message,1);
|
|
|
+ // 普通用户房间:并行发送(使用快照遍历,避免并发修改)
|
|
|
+ // ConcurrentHashMap 的 entrySet() 是弱一致性的,但为了更安全,我们显式创建快照
|
|
|
+ for (Map.Entry<Long, Session> entry : room.entrySet()) {
|
|
|
+ Session session = entry.getValue();
|
|
|
+ if (session != null && session.isOpen()) {
|
|
|
+ sendWithRetry(session, message, 1);
|
|
|
}
|
|
|
- });
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 广播消息
|
|
|
* @param liveId 直播间ID
|
|
|
* @param message 消息内容
|
|
|
+ * 优化:使用快照遍历,避免在遍历过程中修改集合
|
|
|
*/
|
|
|
public void broadcastMessage(Long liveId, String message) {
|
|
|
ConcurrentHashMap<Long, Session> room = getRoom(liveId);
|
|
|
List<Session> adminRoom = getAdminRoom(liveId);
|
|
|
|
|
|
- // 普通用户房间:并行发送
|
|
|
- room.forEach((k, v) -> {
|
|
|
- if (v.isOpen()) {
|
|
|
- sendWithRetry(v,message,1);
|
|
|
+ // 普通用户房间:并行发送(使用快照遍历,避免并发修改)
|
|
|
+ if (!room.isEmpty()) {
|
|
|
+ for (Map.Entry<Long, Session> entry : room.entrySet()) {
|
|
|
+ Session session = entry.getValue();
|
|
|
+ if (session != null && session.isOpen()) {
|
|
|
+ sendWithRetry(session, message, 1);
|
|
|
+ }
|
|
|
}
|
|
|
- });
|
|
|
+ }
|
|
|
|
|
|
// admin房间:串行发送,使用单线程执行器
|
|
|
if (!adminRoom.isEmpty()) {
|
|
|
@@ -696,23 +754,39 @@ public class WebSocketServer {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/**
|
|
|
* 定时清理无效会话(每分钟执行一次)
|
|
|
* 检查心跳超时的会话并关闭
|
|
|
+ * 优化:使用快照遍历,避免在遍历过程中修改集合
|
|
|
*/
|
|
|
@Scheduled(fixedRate = 60000) // 每分钟执行一次
|
|
|
public void cleanInactiveSessions() {
|
|
|
long currentTime = System.currentTimeMillis();
|
|
|
int cleanedCount = 0;
|
|
|
|
|
|
- // 遍历所有直播间
|
|
|
+ // 遍历所有直播间(使用快照,避免在遍历过程中被修改影响)
|
|
|
for (Map.Entry<Long, ConcurrentHashMap<Long, Session>> roomEntry : rooms.entrySet()) {
|
|
|
Long liveId = roomEntry.getKey();
|
|
|
ConcurrentHashMap<Long, Session> room = roomEntry.getValue();
|
|
|
+
|
|
|
+ // 如果房间为空,跳过
|
|
|
+ if (room.isEmpty()) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
|
|
|
- // 检查普通用户会话
|
|
|
+ // 检查普通用户会话(使用快照遍历,避免并发修改异常)
|
|
|
List<Long> toRemove = new ArrayList<>();
|
|
|
- room.forEach((userId, session) -> {
|
|
|
+ // 创建快照,避免在遍历过程中修改原集合
|
|
|
+ for (Map.Entry<Long, Session> userEntry : room.entrySet()) {
|
|
|
+ Long userId = userEntry.getKey();
|
|
|
+ Session session = userEntry.getValue();
|
|
|
+
|
|
|
+ if (session == null) {
|
|
|
+ toRemove.add(userId);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
Long lastHeartbeat = heartbeatCache.get(session.getId());
|
|
|
if (lastHeartbeat != null && (currentTime - lastHeartbeat) > HEARTBEAT_TIMEOUT) {
|
|
|
toRemove.add(userId);
|
|
|
@@ -720,16 +794,35 @@ public class WebSocketServer {
|
|
|
if (session.isOpen()) {
|
|
|
session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "心跳超时"));
|
|
|
}
|
|
|
+
|
|
|
+ // 计算并更新用户在线时长(心跳超时断开连接)
|
|
|
+ Map<String, Object> userProperties = session.getUserProperties();
|
|
|
+ long companyId = -1L;
|
|
|
+ long companyUserId = -1L;
|
|
|
+ if (!Objects.isNull(userProperties.get("companyId"))) {
|
|
|
+ companyId = (long) userProperties.get("companyId");
|
|
|
+ }
|
|
|
+ if (!Objects.isNull(userProperties.get("companyUserId"))) {
|
|
|
+ companyUserId = (long) userProperties.get("companyUserId");
|
|
|
+ }
|
|
|
+ updateUserOnlineDuration(liveId, userId, companyId, companyUserId);
|
|
|
} catch (Exception e) {
|
|
|
log.error("关闭超时会话失败: sessionId={}, liveId={}, userId={}",
|
|
|
session.getId(), liveId, userId, e);
|
|
|
}
|
|
|
}
|
|
|
- });
|
|
|
+ }
|
|
|
|
|
|
// 移除超时的会话
|
|
|
- toRemove.forEach(room::remove);
|
|
|
- cleanedCount += toRemove.size();
|
|
|
+ if (!toRemove.isEmpty()) {
|
|
|
+ String hashKey = String.format(LiveKeysConstant.LIVE_WATCH_USERS, liveId);
|
|
|
+ for (Long userId : toRemove) {
|
|
|
+ room.remove(userId);
|
|
|
+ // 从 Redis hash 中删除无效用户
|
|
|
+ redisCache.hashDelete(hashKey, String.valueOf(userId));
|
|
|
+ }
|
|
|
+ cleanedCount += toRemove.size();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
// 检查admin房间
|
|
|
@@ -770,14 +863,22 @@ public class WebSocketServer {
|
|
|
* 广播点赞消息
|
|
|
* @param liveId 直播间ID
|
|
|
* @param message 消息内容
|
|
|
+ * 优化:使用快照遍历,避免在遍历过程中修改集合
|
|
|
*/
|
|
|
public void broadcastLikeMessage(Long liveId, String message) {
|
|
|
ConcurrentHashMap<Long, Session> room = getRoom(liveId);
|
|
|
- room.forEach((k, v) -> {
|
|
|
- if (v.isOpen()) {
|
|
|
- sendWithRetry(v,message,1);
|
|
|
+
|
|
|
+ if (room.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用快照遍历,避免并发修改
|
|
|
+ for (Map.Entry<Long, Session> entry : room.entrySet()) {
|
|
|
+ Session session = entry.getValue();
|
|
|
+ if (session != null && session.isOpen()) {
|
|
|
+ sendWithRetry(session, message, 1);
|
|
|
}
|
|
|
- });
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private void sendWithRetry(Session session, String message, int maxRetries) {
|
|
|
@@ -919,5 +1020,165 @@ public class WebSocketServer {
|
|
|
redisCache.redisTemplate.opsForZSet().removeRangeByScore(key + liveId, data, data);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 计算并更新用户在线时长
|
|
|
+ * @param liveId 直播间ID
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @param companyId 公司ID
|
|
|
+ * @param companyUserId 销售ID
|
|
|
+ */
|
|
|
+ private void updateUserOnlineDuration(Long liveId, Long userId, Long companyId, Long companyUserId) {
|
|
|
+ try {
|
|
|
+ // 从 Redis 获取用户进入时间
|
|
|
+ String entryTimeKey = String.format(USER_ENTRY_TIME_KEY, liveId, userId);
|
|
|
+ Long entryTime = redisCache.getCacheObject(entryTimeKey);
|
|
|
+
|
|
|
+ if (entryTime == null) {
|
|
|
+ // 如果没有进入时间记录,可能是旧数据,跳过
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ long currentTimeMillis = System.currentTimeMillis();
|
|
|
+ Date now = new Date();
|
|
|
+
|
|
|
+ // 计算在线时长(秒)
|
|
|
+ long durationSeconds = (currentTimeMillis - entryTime) / 1000;
|
|
|
+
|
|
|
+ if (durationSeconds <= 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取当前直播/回放状态
|
|
|
+ Map<String, Integer> flagMap = liveWatchUserService.getLiveFlagWithCache(liveId);
|
|
|
+ Integer currentLiveFlag = flagMap.get("liveFlag");
|
|
|
+ Integer currentReplayFlag = flagMap.get("replayFlag");
|
|
|
+
|
|
|
+ // 查询用户记录
|
|
|
+ LiveWatchUserEntry liveWatchUser = liveWatchUserService.selectLiveWatchAndCompanyUserByFlag(
|
|
|
+ liveId, userId, currentLiveFlag, currentReplayFlag);
|
|
|
+
|
|
|
+ if (liveWatchUser != null) {
|
|
|
+ // 累加在线时长
|
|
|
+ Long onlineSeconds = liveWatchUser.getOnlineSeconds();
|
|
|
+ if (onlineSeconds == null) {
|
|
|
+ onlineSeconds = 0L;
|
|
|
+ }
|
|
|
+ liveWatchUser.setOnlineSeconds(onlineSeconds + durationSeconds);
|
|
|
+ liveWatchUser.setUpdateTime(now);
|
|
|
+
|
|
|
+ // 更新数据库
|
|
|
+ liveWatchUserService.updateLiveWatchUserEntry(liveWatchUser);
|
|
|
+
|
|
|
+ // 如果 LiveWatchUserEntry 存在,并且当前是直播状态(liveFlag = 1),更新 LiveWatchLog
|
|
|
+ if (currentLiveFlag != null && currentLiveFlag == 1
|
|
|
+ && liveWatchUser.getCompanyId() != null && liveWatchUser.getCompanyId() > 0
|
|
|
+ && liveWatchUser.getCompanyUserId() != null && liveWatchUser.getCompanyUserId() > 0) {
|
|
|
+ updateLiveWatchLogTypeByDuration(liveId, userId,
|
|
|
+ liveWatchUser.getCompanyId(), liveWatchUser.getCompanyUserId(),
|
|
|
+ liveWatchUser.getOnlineSeconds());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 删除 Redis 中的进入时间记录
|
|
|
+ redisCache.deleteObject(entryTimeKey);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("更新用户在线时长异常:liveId={}, userId={}, error={}",
|
|
|
+ liveId, userId, e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 在连接时更新 LiveWatchLog 的 logType
|
|
|
+ * 如果 logType 类型不是 2,修改 logType 类型为 1(看课中)
|
|
|
+ */
|
|
|
+ private void updateLiveWatchLogTypeOnConnect(Long liveId, Long userId, Long companyId, Long companyUserId) {
|
|
|
+ try {
|
|
|
+ LiveWatchLog queryLog = new LiveWatchLog();
|
|
|
+ queryLog.setLiveId(liveId);
|
|
|
+ queryLog.setUserId(userId);
|
|
|
+ queryLog.setCompanyId(companyId);
|
|
|
+ queryLog.setCompanyUserId(companyUserId);
|
|
|
+
|
|
|
+ List<LiveWatchLog> logs = liveWatchLogService.selectLiveWatchLogList(queryLog);
|
|
|
+ if (logs != null && !logs.isEmpty()) {
|
|
|
+ for (LiveWatchLog log : logs) {
|
|
|
+ // 如果 logType 不是 2(完课),则更新为 1(看课中)
|
|
|
+ if (log.getLogType() == null || log.getLogType() != 2) {
|
|
|
+ log.setLogType(1);
|
|
|
+ liveWatchLogService.updateLiveWatchLog(log);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("更新 LiveWatchLog logType 异常(连接时):liveId={}, userId={}, error={}",
|
|
|
+ liveId, userId, e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据在线时长更新 LiveWatchLog 的 logType
|
|
|
+ * @param liveId 直播间ID
|
|
|
+ * @param userId 用户ID
|
|
|
+ * @param companyId 公司ID
|
|
|
+ * @param companyUserId 销售ID
|
|
|
+ * @param onlineSeconds 在线时长(秒)
|
|
|
+ */
|
|
|
+ private void updateLiveWatchLogTypeByDuration(Long liveId, Long userId, Long companyId,
|
|
|
+ Long companyUserId, Long onlineSeconds) {
|
|
|
+ try {
|
|
|
+ // 获取直播视频总时长(videoType = 1 的视频)
|
|
|
+ List<LiveVideo> videos = liveVideoService.listByLiveId(liveId, 1);
|
|
|
+ long totalVideoDuration = 0L;
|
|
|
+ if (videos != null && !videos.isEmpty()) {
|
|
|
+ totalVideoDuration = videos.stream()
|
|
|
+ .filter(v -> v.getDuration() != null)
|
|
|
+ .mapToLong(LiveVideo::getDuration)
|
|
|
+ .sum();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询 LiveWatchLog
|
|
|
+ LiveWatchLog queryLog = new LiveWatchLog();
|
|
|
+ queryLog.setLiveId(liveId);
|
|
|
+ queryLog.setUserId(userId);
|
|
|
+ queryLog.setCompanyId(companyId);
|
|
|
+ queryLog.setCompanyUserId(companyUserId);
|
|
|
+
|
|
|
+ List<LiveWatchLog> logs = liveWatchLogService.selectLiveWatchLogList(queryLog);
|
|
|
+ if (logs == null || logs.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (LiveWatchLog log : logs) {
|
|
|
+ boolean needUpdate = false;
|
|
|
+ Integer newLogType = log.getLogType();
|
|
|
+
|
|
|
+ // ① 如果在线时长 <= 3分钟,修改 logType 为 4(看课中断)
|
|
|
+ if (onlineSeconds <= 180) { // 3分钟 = 180秒
|
|
|
+ newLogType = 4;
|
|
|
+ needUpdate = true;
|
|
|
+ }
|
|
|
+ // ③ 如果直播视频 >= 40分钟,在线时长 >= 30分钟,logType 设置为 2(完课)
|
|
|
+ else if (totalVideoDuration >= 2400 && onlineSeconds >= 1800) { // 40分钟 = 2400秒,30分钟 = 1800秒
|
|
|
+ newLogType = 2;
|
|
|
+ needUpdate = true;
|
|
|
+ }
|
|
|
+ // 如果直播视频 >= 20分钟且 < 40分钟,在线时长 >= 20分钟,logType 设置为 2(完课)
|
|
|
+ else if (totalVideoDuration >= 1200 && totalVideoDuration < 2400 && onlineSeconds >= 1200) { // 20分钟 = 1200秒
|
|
|
+ newLogType = 2;
|
|
|
+ needUpdate = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果 logType 已经是 2(完课),不再更新
|
|
|
+ if (needUpdate && (log.getLogType() == null || log.getLogType() != 2)) {
|
|
|
+ log.setLogType(newLogType);
|
|
|
+ liveWatchLogService.updateLiveWatchLog(log);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("根据在线时长更新 LiveWatchLog logType 异常:liveId={}, userId={}, error={}",
|
|
|
+ liveId, userId, e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|