package com.tcm.training.admin.app.home.service;
|
|
import cn.hutool.core.util.StrUtil;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.tcm.training.admin.app.live.support.AppLiveCourseDisplaySupport;
|
import com.tcm.training.admin.app.home.vo.AppHomeLiveCourseVO;
|
import com.tcm.training.admin.live.entity.LiveCourse;
|
import com.tcm.training.admin.live.entity.LiveSession;
|
import com.tcm.training.admin.live.mapper.LiveCourseMapper;
|
import com.tcm.training.admin.live.mapper.LiveSessionMapper;
|
import org.springframework.stereotype.Service;
|
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.Comparator;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
/**
|
* APP 首页养生直播聚合服务。
|
*/
|
@Service
|
public class AppHomeLiveCourseService {
|
|
private static final int COURSE_STATUS_ENABLED = 1;
|
private static final long NEXT_START_DELAY_MINUTES = 10L;
|
private static final int HOME_CARD_LIMIT = 2;
|
private static final DateTimeFormatter START_TIME_FORMATTER = DateTimeFormatter.ofPattern("MM月dd日 HH:mm 开播");
|
|
private final LiveCourseMapper liveCourseMapper;
|
private final LiveSessionMapper liveSessionMapper;
|
private final AppLiveCourseDisplaySupport appLiveCourseDisplaySupport;
|
|
/**
|
* 注入首页直播所需的课程与场次查询能力。
|
*/
|
public AppHomeLiveCourseService(LiveCourseMapper liveCourseMapper,
|
LiveSessionMapper liveSessionMapper,
|
AppLiveCourseDisplaySupport appLiveCourseDisplaySupport) {
|
this.liveCourseMapper = liveCourseMapper;
|
this.liveSessionMapper = liveSessionMapper;
|
this.appLiveCourseDisplaySupport = appLiveCourseDisplaySupport;
|
}
|
|
/**
|
* 查询 APP 首页展示的养生直播卡片。
|
*/
|
public List<AppHomeLiveCourseVO> listHomeLiveCourses() {
|
List<LiveCourse> courses = liveCourseMapper.selectList(new LambdaQueryWrapper<LiveCourse>()
|
.eq(LiveCourse::getDeleted, 0)
|
.eq(LiveCourse::getStatus, COURSE_STATUS_ENABLED)
|
.orderByDesc(LiveCourse::getId));
|
if (courses == null || courses.isEmpty()) {
|
return Collections.emptyList();
|
}
|
|
Map<Long, List<LiveSession>> sessionMap = loadSessionMap(courses);
|
LocalDateTime now = LocalDateTime.now();
|
List<HomeLiveCourseCard> cards = new ArrayList<HomeLiveCourseCard>(courses.size());
|
for (LiveCourse course : courses) {
|
cards.add(buildHomeCard(course, sessionMap.get(course.getId()), now));
|
}
|
cards.sort(new Comparator<HomeLiveCourseCard>() {
|
@Override
|
public int compare(HomeLiveCourseCard left, HomeLiveCourseCard right) {
|
return compareCards(left, right);
|
}
|
});
|
|
List<AppHomeLiveCourseVO> result = new ArrayList<AppHomeLiveCourseVO>(Math.min(cards.size(), HOME_CARD_LIMIT));
|
for (int i = 0; i < cards.size() && i < HOME_CARD_LIMIT; i++) {
|
result.add(cards.get(i).getCard());
|
}
|
return result;
|
}
|
|
/**
|
* 批量加载首页直播课程的全部场次。
|
*/
|
private Map<Long, List<LiveSession>> loadSessionMap(List<LiveCourse> courses) {
|
List<Long> courseIds = new ArrayList<Long>(courses.size());
|
for (LiveCourse course : courses) {
|
courseIds.add(course.getId());
|
}
|
List<LiveSession> sessions = liveSessionMapper.selectList(new LambdaQueryWrapper<LiveSession>()
|
.in(LiveSession::getLiveCourseId, courseIds)
|
.eq(LiveSession::getDeleted, 0)
|
.orderByAsc(LiveSession::getStartTime)
|
.orderByAsc(LiveSession::getId));
|
return appLiveCourseDisplaySupport.groupSessionsByCourseId(sessions);
|
}
|
|
/**
|
* 根据课程与场次快照构造首页直播卡片。
|
*/
|
private HomeLiveCourseCard buildHomeCard(LiveCourse course, List<LiveSession> sessions, LocalDateTime now) {
|
HomeLiveCourseCard homeCard = new HomeLiveCourseCard();
|
AppHomeLiveCourseVO card = new AppHomeLiveCourseVO();
|
String coverImage = appLiveCourseDisplaySupport.trimToNull(course.getCoverImg());
|
String liveImage = appLiveCourseDisplaySupport.resolveLiveImage(course);
|
card.setCourseId(course.getId());
|
card.setTitle(appLiveCourseDisplaySupport.trimToNull(course.getTitle()));
|
card.setCoverImage(coverImage);
|
card.setLiveImage(liveImage);
|
card.setHasPassword(StrUtil.isNotBlank(course.getPassword()));
|
homeCard.setCard(card);
|
|
AppLiveCourseDisplaySupport.CourseDisplaySnapshot snapshot =
|
appLiveCourseDisplaySupport.resolveDisplay(sessions, now, NEXT_START_DELAY_MINUTES);
|
LiveSession currentSession = snapshot.getCurrentSession();
|
if (currentSession != null) {
|
card.setDisplayStatus(AppLiveCourseDisplaySupport.DISPLAY_STATUS_LIVING);
|
card.setDisplayStatusText("直播中");
|
card.setDisplayImage(appLiveCourseDisplaySupport.resolveLivingDisplayImage(coverImage, liveImage));
|
card.setCurrentSessionId(currentSession.getId());
|
card.setVideoUrl(appLiveCourseDisplaySupport.trimToNull(currentSession.getVideoUrl()));
|
homeCard.setStatusRank(0);
|
return homeCard;
|
}
|
|
homeCard.setLatestEndedAt(snapshot.getLatestEndedAt());
|
if (snapshot.isEndedCooldown()) {
|
card.setDisplayStatus(AppLiveCourseDisplaySupport.DISPLAY_STATUS_ENDED);
|
card.setDisplayStatusText("已结束");
|
card.setDisplayImage(appLiveCourseDisplaySupport.resolveStaticDisplayImage(coverImage, liveImage));
|
homeCard.setStatusRank(2);
|
return homeCard;
|
}
|
|
LiveSession nextSession = snapshot.getNextSession();
|
if (nextSession != null) {
|
card.setDisplayStatus(AppLiveCourseDisplaySupport.DISPLAY_STATUS_NOT_STARTED);
|
card.setNextStartTime(nextSession.getStartTime());
|
card.setDisplayStatusText(formatNextStartTime(nextSession.getStartTime()));
|
card.setDisplayImage(appLiveCourseDisplaySupport.resolveStaticDisplayImage(coverImage, liveImage));
|
homeCard.setStatusRank(1);
|
homeCard.setNextStartTime(nextSession.getStartTime());
|
return homeCard;
|
}
|
|
card.setDisplayStatus(AppLiveCourseDisplaySupport.DISPLAY_STATUS_ENDED);
|
card.setDisplayStatusText("已结束");
|
card.setDisplayImage(appLiveCourseDisplaySupport.resolveStaticDisplayImage(coverImage, liveImage));
|
homeCard.setStatusRank(2);
|
return homeCard;
|
}
|
|
/**
|
* 将开播时间格式化为首页展示文案。
|
*/
|
private String formatNextStartTime(LocalDateTime startTime) {
|
if (startTime == null) {
|
return "已结束";
|
}
|
return START_TIME_FORMATTER.format(startTime);
|
}
|
|
/**
|
* 对首页直播卡片执行统一排序。
|
*/
|
private int compareCards(HomeLiveCourseCard left, HomeLiveCourseCard right) {
|
int rankCompare = Integer.compare(left.getStatusRank(), right.getStatusRank());
|
if (rankCompare != 0) {
|
return rankCompare;
|
}
|
|
if (left.getStatusRank() == 1) {
|
int nextStartCompare = appLiveCourseDisplaySupport.compareSessionTime(left.getNextStartTime(), right.getNextStartTime());
|
if (nextStartCompare != 0) {
|
return nextStartCompare;
|
}
|
}
|
|
if (left.getStatusRank() == 2) {
|
int endedCompare = appLiveCourseDisplaySupport.compareSessionTime(right.getLatestEndedAt(), left.getLatestEndedAt());
|
if (endedCompare != 0) {
|
return endedCompare;
|
}
|
}
|
|
return Long.compare(right.getCard().getCourseId(), left.getCard().getCourseId());
|
}
|
|
/**
|
* 首页直播卡片排序中间态。
|
*/
|
private static class HomeLiveCourseCard {
|
|
private AppHomeLiveCourseVO card;
|
private int statusRank;
|
private LocalDateTime nextStartTime;
|
private LocalDateTime latestEndedAt;
|
|
/**
|
* 获取首页直播卡片响应。
|
*/
|
public AppHomeLiveCourseVO getCard() {
|
return card;
|
}
|
|
/**
|
* 设置首页直播卡片响应。
|
*/
|
public void setCard(AppHomeLiveCourseVO card) {
|
this.card = card;
|
}
|
|
/**
|
* 获取状态优先级。
|
*/
|
public int getStatusRank() {
|
return statusRank;
|
}
|
|
/**
|
* 设置状态优先级。
|
*/
|
public void setStatusRank(int statusRank) {
|
this.statusRank = statusRank;
|
}
|
|
/**
|
* 获取下一次开播时间。
|
*/
|
public LocalDateTime getNextStartTime() {
|
return nextStartTime;
|
}
|
|
/**
|
* 设置下一次开播时间。
|
*/
|
public void setNextStartTime(LocalDateTime nextStartTime) {
|
this.nextStartTime = nextStartTime;
|
}
|
|
/**
|
* 获取最近结束时间。
|
*/
|
public LocalDateTime getLatestEndedAt() {
|
return latestEndedAt;
|
}
|
|
/**
|
* 设置最近结束时间。
|
*/
|
public void setLatestEndedAt(LocalDateTime latestEndedAt) {
|
this.latestEndedAt = latestEndedAt;
|
}
|
}
|
}
|