zjk
2026-09-18 e429c291995f3f20184079e480031533ae5c03e6
tcm-training-admin-backend/src/main/java/com/tcm/training/admin/live/service/LiveSessionStatusSyncService.java
@@ -10,8 +10,10 @@
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Service
@@ -46,7 +48,8 @@
                .orderByAsc(LiveSession::getId));
        int startedCount = 0;
        if (dueSessions != null && !dueSessions.isEmpty()) {
            for (LiveSession dueSession : dueSessions) {
            List<LiveSession> effectiveDueSessions = pickEffectiveDueSessions(dueSessions);
            for (LiveSession dueSession : effectiveDueSessions) {
                // 定时任务与人工开播共用同一套状态迁移逻辑,避免状态口径分叉。
                LiveSessionStatusVO statusVO = liveSessionAppService.autoStart(dueSession.getId(), now);
                if (statusVO == null) {
@@ -58,7 +61,8 @@
            }
            if (startedCount > 0) {
                log.info("live session auto start finished, scanned={}, started={}", dueSessions.size(), startedCount);
                log.info("live session auto start finished, scanned={}, effective={}, started={}",
                        dueSessions.size(), effectiveDueSessions.size(), startedCount);
            }
        }
@@ -66,6 +70,63 @@
    }
    /**
     * 同一课程只挑一条最应被自动开播的场次,避免历史过期开播记录被批量拉起后又互相收敛。
     */
    private List<LiveSession> pickEffectiveDueSessions(List<LiveSession> dueSessions) {
        if (dueSessions == null || dueSessions.isEmpty()) {
            return dueSessions;
        }
        Map<Long, LiveSession> effectiveSessionMap = new LinkedHashMap<Long, LiveSession>();
        for (LiveSession dueSession : dueSessions) {
            if (dueSession == null || dueSession.getLiveCourseId() == null) {
                continue;
            }
            LiveSession current = effectiveSessionMap.get(dueSession.getLiveCourseId());
            if (current == null || compareDueSession(current, dueSession) < 0) {
                effectiveSessionMap.put(dueSession.getLiveCourseId(), dueSession);
            }
        }
        return new java.util.ArrayList<LiveSession>(effectiveSessionMap.values());
    }
    /**
     * 自动开播候选按计划开播时间排序,时间更晚者优先;同时间取 ID 更大的最新记录。
     */
    private int compareDueSession(LiveSession left, LiveSession right) {
        LocalDateTime leftTime = left == null ? null : left.getStartTime();
        LocalDateTime rightTime = right == null ? null : right.getStartTime();
        if (leftTime == null && rightTime == null) {
            return compareSessionId(left, right);
        }
        if (leftTime == null) {
            return -1;
        }
        if (rightTime == null) {
            return 1;
        }
        int timeCompare = leftTime.compareTo(rightTime);
        if (timeCompare != 0) {
            return timeCompare;
        }
        return compareSessionId(left, right);
    }
    private int compareSessionId(LiveSession left, LiveSession right) {
        Long leftId = left == null ? null : left.getId();
        Long rightId = right == null ? null : right.getId();
        if (leftId == null && rightId == null) {
            return 0;
        }
        if (leftId == null) {
            return -1;
        }
        if (rightId == null) {
            return 1;
        }
        return leftId.compareTo(rightId);
    }
    /**
     * 定时收敛历史残留的直播中场次,避免同课程出现多条或过期直播中状态。
     */
    private void reconcileLivingCourses(LocalDateTime now) {