package com.tcm.training.admin.live.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.tcm.training.admin.live.entity.LiveSession; import com.tcm.training.admin.live.mapper.LiveSessionMapper; import com.tcm.training.admin.live.vo.LiveSessionStatusVO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.time.LocalDateTime; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; @Service public class LiveSessionStatusSyncService { private static final Logger log = LoggerFactory.getLogger(LiveSessionStatusSyncService.class); private final LiveSessionMapper liveSessionMapper; private final LiveSessionAppService liveSessionAppService; /** * 注入直播场次查询能力与状态流转服务。 */ public LiveSessionStatusSyncService(LiveSessionMapper liveSessionMapper, LiveSessionAppService liveSessionAppService) { this.liveSessionMapper = liveSessionMapper; this.liveSessionAppService = liveSessionAppService; } /** * 扫描已到开播时间的未开始场次,并自动切换为直播中。 */ @Scheduled(fixedDelayString = "${app.live.session-status-sync-interval-ms:60000}") public void syncDueSessions() { LocalDateTime now = LocalDateTime.now(); List dueSessions = liveSessionMapper.selectList(new LambdaQueryWrapper() .eq(LiveSession::getDeleted, 0) .eq(LiveSession::getLiveStatus, 0) .isNotNull(LiveSession::getStartTime) .le(LiveSession::getStartTime, now) .orderByAsc(LiveSession::getStartTime) .orderByAsc(LiveSession::getId)); int startedCount = 0; if (dueSessions != null && !dueSessions.isEmpty()) { for (LiveSession dueSession : dueSessions) { // 定时任务与人工开播共用同一套状态迁移逻辑,避免状态口径分叉。 LiveSessionStatusVO statusVO = liveSessionAppService.autoStart(dueSession.getId(), now); if (statusVO == null) { log.warn("live session auto start skipped, sessionId={}, courseId={}", dueSession.getId(), dueSession.getLiveCourseId()); continue; } startedCount++; } if (startedCount > 0) { log.info("live session auto start finished, scanned={}, started={}", dueSessions.size(), startedCount); } } reconcileLivingCourses(now); } /** * 定时收敛历史残留的直播中场次,避免同课程出现多条或过期直播中状态。 */ private void reconcileLivingCourses(LocalDateTime now) { List livingSessions = liveSessionMapper.selectList(new LambdaQueryWrapper() .eq(LiveSession::getDeleted, 0) .eq(LiveSession::getLiveStatus, 1) .orderByAsc(LiveSession::getLiveCourseId) .orderByAsc(LiveSession::getId)); if (livingSessions == null || livingSessions.isEmpty()) { return; } Set courseIds = new LinkedHashSet(); for (LiveSession livingSession : livingSessions) { if (livingSession == null || livingSession.getLiveCourseId() == null) { continue; } courseIds.add(livingSession.getLiveCourseId()); } int repairedCount = 0; for (Long courseId : courseIds) { repairedCount += liveSessionAppService.reconcileCourseLivingSessions(courseId, now); } if (repairedCount > 0) { log.info("live session reconcile finished, livingCourses={}, repaired={}", courseIds.size(), repairedCount); } } }