package com.tcm.training.admin.app.live.support; import com.tcm.training.admin.live.entity.LiveCourse; import com.tcm.training.admin.live.entity.LiveSession; import lombok.Getter; import lombok.Setter; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * APP 养生直播展示态解析工具。 */ @Component public class AppLiveCourseDisplaySupport { public static final int SESSION_STATUS_NOT_STARTED = 0; public static final int SESSION_STATUS_LIVING = 1; public static final int SESSION_STATUS_ENDED = 2; public static final String DISPLAY_STATUS_NOT_STARTED = "NOT_STARTED"; public static final String DISPLAY_STATUS_LIVING = "LIVING"; public static final String DISPLAY_STATUS_ENDED = "ENDED"; /** * 统一解析一门直播课程在当前时刻的展示态快照。 */ public CourseDisplaySnapshot resolveDisplay(List sessions, LocalDateTime now, long endedCooldownMinutes) { List safeSessions = sessions == null ? Collections.emptyList() : sessions; CourseDisplaySnapshot snapshot = new CourseDisplaySnapshot(); LiveSession latestStartedSession = findLatestStartedSession(safeSessions, now); snapshot.setLatestStartedSession(latestStartedSession); LiveSession currentSession = resolveCurrentSession(latestStartedSession); snapshot.setCurrentSession(currentSession); if (currentSession != null) { snapshot.setDisplayStatus(DISPLAY_STATUS_LIVING); return snapshot; } LiveSession latestEndedSession = resolveLatestEndedSession(safeSessions, latestStartedSession); snapshot.setLatestEndedSession(latestEndedSession); snapshot.setLatestEndedAt(resolveEndedAt(latestEndedSession)); if (isInEndedCooldown(latestEndedSession, now, endedCooldownMinutes)) { snapshot.setDisplayStatus(DISPLAY_STATUS_ENDED); snapshot.setEndedCooldown(true); return snapshot; } LiveSession nextSession = findNextSession(safeSessions, now); snapshot.setNextSession(nextSession); if (nextSession != null) { snapshot.setDisplayStatus(DISPLAY_STATUS_NOT_STARTED); return snapshot; } snapshot.setDisplayStatus(DISPLAY_STATUS_ENDED); return snapshot; } /** * 将直播场次按课程 ID 聚合,便于首页与详情复用。 */ public Map> groupSessionsByCourseId(List sessions) { Map> sessionMap = new HashMap>(); if (sessions == null || sessions.isEmpty()) { return sessionMap; } for (LiveSession session : sessions) { List courseSessions = sessionMap.get(session.getLiveCourseId()); if (courseSessions == null) { courseSessions = new ArrayList(); sessionMap.put(session.getLiveCourseId(), courseSessions); } courseSessions.add(session); } return sessionMap; } /** * 选择课程宣传图首图作为直播态展示图。 */ public String resolveLiveImage(LiveCourse course) { if (course == null) { return null; } if (course.getBannerImgs() != null) { for (String bannerImage : course.getBannerImgs()) { String normalized = trimToNull(bannerImage); if (normalized != null) { return normalized; } } } return trimToNull(course.getCoverImg()); } /** * 解析直播中或结束冷却期的展示图。 */ public String resolveLivingDisplayImage(String coverImage, String liveImage) { return liveImage == null ? coverImage : liveImage; } /** * 解析未开始或完全结束态的展示图。 */ public String resolveStaticDisplayImage(String coverImage, String liveImage) { return coverImage == null ? liveImage : coverImage; } /** * 获取直播结束时间,优先使用实际结束时间。 */ public LocalDateTime resolveEndedAt(LiveSession session) { if (session == null) { return null; } if (session.getActualEndTime() != null) { return session.getActualEndTime(); } return session.getStartTime(); } /** * 获取直播中场次的排序时间,优先使用实际开播时间。 */ public LocalDateTime resolveCurrentSessionTime(LiveSession session) { if (session == null) { return null; } if (session.getActualStartTime() != null) { return session.getActualStartTime(); } return session.getStartTime(); } /** * 判断最近结束场次是否仍处于冷却窗口。 */ public boolean isInEndedCooldown(LiveSession latestEndedSession, LocalDateTime now, long endedCooldownMinutes) { LocalDateTime endedAt = resolveEndedAt(latestEndedSession); if (endedAt == null) { return false; } return endedAt.plusMinutes(endedCooldownMinutes).isAfter(now); } /** * 归一化字符串,空白值统一转为 null。 */ public String trimToNull(String value) { if (value == null) { return null; } String trimmed = value.trim(); return trimmed.isEmpty() ? null : trimmed; } /** * 将可空整型归一化为默认值 0。 */ public int defaultNumber(Integer value) { return value == null ? 0 : value.intValue(); } /** * 比较两个时间值,null 视为最弱排序值。 */ public int compareSessionTime(LocalDateTime left, LocalDateTime right) { if (left == null && right == null) { return 0; } if (left == null) { return 1; } if (right == null) { return -1; } return left.compareTo(right); } /** * 选择时间线上最近一场已经开始的场次。 */ private LiveSession findLatestStartedSession(List sessions, LocalDateTime now) { LiveSession result = null; for (LiveSession session : sessions) { if (session.getStartTime() == null || session.getStartTime().isAfter(now)) { continue; } if (result == null || compareStartedSession(result, session) < 0) { result = session; } } return result; } /** * 将时间线上最近一场已开始场次解析为当前直播场次。 */ private LiveSession resolveCurrentSession(LiveSession latestStartedSession) { if (latestStartedSession == null) { return null; } if (defaultNumber(latestStartedSession.getLiveStatus()) != SESSION_STATUS_LIVING) { return null; } return latestStartedSession; } /** * 解析最近一场已结束场次,优先使用时间线上的最新已开始场次。 */ private LiveSession resolveLatestEndedSession(List sessions, LiveSession latestStartedSession) { if (latestStartedSession != null && defaultNumber(latestStartedSession.getLiveStatus()) == SESSION_STATUS_ENDED) { return latestStartedSession; } LiveSession result = null; for (LiveSession session : sessions) { if (defaultNumber(session.getLiveStatus()) != SESSION_STATUS_ENDED) { continue; } if (result == null || compareEndedSession(result, session) < 0) { result = session; } } return result; } /** * 选择下一场待开播的场次。 */ private LiveSession findNextSession(List sessions, LocalDateTime now) { LiveSession result = null; for (LiveSession session : sessions) { if (defaultNumber(session.getLiveStatus()) != SESSION_STATUS_NOT_STARTED) { continue; } if (session.getStartTime() == null || !session.getStartTime().isAfter(now)) { continue; } if (result == null || compareSessionTime(session.getStartTime(), result.getStartTime()) < 0) { result = session; } } return result; } /** * 比较两场已开始场次的时间线先后,开始时间更晚者优先。 */ private int compareStartedSession(LiveSession left, LiveSession right) { int startTimeCompare = compareSessionTime(resolveCurrentSessionTime(left), resolveCurrentSessionTime(right)); if (startTimeCompare != 0) { return startTimeCompare; } return compareSessionId(left, right); } /** * 比较两场已结束场次的时间线先后,结束时间更晚者优先。 */ private int compareEndedSession(LiveSession left, LiveSession right) { int endedAtCompare = compareSessionTime(resolveEndedAt(left), resolveEndedAt(right)); if (endedAtCompare != 0) { return endedAtCompare; } 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); } /** * 课程展示态快照。 */ @Getter @Setter public static class CourseDisplaySnapshot { private String displayStatus; private LiveSession currentSession; private LiveSession latestStartedSession; private LiveSession latestEndedSession; private LiveSession nextSession; private LocalDateTime latestEndedAt; private boolean endedCooldown; } }