package com.tcm.training.admin.live.service;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.tcm.training.admin.common.DateTimes;
|
import com.tcm.training.admin.common.PageResponse;
|
import com.tcm.training.admin.live.dto.LiveSessionQuestionRequest;
|
import com.tcm.training.admin.live.dto.LiveSessionUpsertRequest;
|
import com.tcm.training.admin.live.entity.LiveCourse;
|
import com.tcm.training.admin.live.entity.LiveSession;
|
import com.tcm.training.admin.live.entity.LiveSessionQuestion;
|
import com.tcm.training.admin.live.entity.LiveReserve;
|
import com.tcm.training.admin.live.mapper.LiveCourseMapper;
|
import com.tcm.training.admin.live.mapper.LiveReserveMapper;
|
import com.tcm.training.admin.live.mapper.LiveSessionMapper;
|
import com.tcm.training.admin.live.mapper.LiveSessionQuestionMapper;
|
import com.tcm.training.admin.live.vo.LivePushNotificationVO;
|
import com.tcm.training.admin.live.vo.LiveSessionQuestionVO;
|
import com.tcm.training.admin.live.vo.LiveSessionStatusVO;
|
import com.tcm.training.admin.live.vo.LiveSessionVO;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import java.time.LocalDateTime;
|
import java.util.ArrayList;
|
import java.util.Collections;
|
import java.util.HashMap;
|
import java.util.HashSet;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
|
@Service
|
public class LiveSessionAppService {
|
|
private final LiveCourseMapper liveCourseMapper;
|
private final LiveSessionMapper liveSessionMapper;
|
private final LiveSessionQuestionMapper liveSessionQuestionMapper;
|
private final LiveReserveMapper liveReserveMapper;
|
|
public LiveSessionAppService(LiveCourseMapper liveCourseMapper,
|
LiveSessionMapper liveSessionMapper,
|
LiveSessionQuestionMapper liveSessionQuestionMapper,
|
LiveReserveMapper liveReserveMapper) {
|
this.liveCourseMapper = liveCourseMapper;
|
this.liveSessionMapper = liveSessionMapper;
|
this.liveSessionQuestionMapper = liveSessionQuestionMapper;
|
this.liveReserveMapper = liveReserveMapper;
|
}
|
|
public PageResponse<LiveSessionVO> pageSessions(Long courseId, int page, int pageSize, String startTimeFrom, String startTimeTo, Integer liveStatus) {
|
requireCourse(courseId);
|
|
LambdaQueryWrapper<LiveSession> qw = new LambdaQueryWrapper<LiveSession>()
|
.eq(LiveSession::getLiveCourseId, courseId);
|
LocalDateTime from = DateTimes.parse(startTimeFrom);
|
LocalDateTime to = DateTimes.parse(startTimeTo);
|
if (from != null) {
|
qw.ge(LiveSession::getStartTime, from);
|
}
|
if (to != null) {
|
qw.le(LiveSession::getStartTime, to);
|
}
|
if (liveStatus != null) {
|
qw.eq(LiveSession::getLiveStatus, liveStatus);
|
}
|
qw.orderByDesc(LiveSession::getStartTime).orderByDesc(LiveSession::getId);
|
|
IPage<LiveSession> p = liveSessionMapper.selectPage(new Page<LiveSession>(page, pageSize), qw);
|
List<LiveSession> sessions = p.getRecords();
|
if (sessions == null || sessions.isEmpty()) {
|
return PageResponse.of(Collections.<LiveSessionVO>emptyList(), p.getTotal(), p.getCurrent(), p.getSize());
|
}
|
|
List<Long> sessionIds = new ArrayList<Long>();
|
for (LiveSession s : sessions) {
|
sessionIds.add(s.getId());
|
}
|
|
List<LiveSessionQuestion> qs = liveSessionQuestionMapper.selectList(new LambdaQueryWrapper<LiveSessionQuestion>()
|
.in(LiveSessionQuestion::getSessionId, sessionIds)
|
.orderByAsc(LiveSessionQuestion::getSort));
|
Map<Long, List<LiveSessionQuestionVO>> map = new HashMap<Long, List<LiveSessionQuestionVO>>();
|
for (LiveSessionQuestion q : qs) {
|
List<LiveSessionQuestionVO> list = map.get(q.getSessionId());
|
if (list == null) {
|
list = new ArrayList<LiveSessionQuestionVO>();
|
map.put(q.getSessionId(), list);
|
}
|
LiveSessionQuestionVO qvo = new LiveSessionQuestionVO();
|
qvo.setId(q.getId());
|
qvo.setSort(q.getSort());
|
qvo.setStem(q.getStem());
|
qvo.setOptionA(q.getOptionA());
|
qvo.setOptionB(q.getOptionB());
|
qvo.setOptionC(q.getOptionC());
|
qvo.setCorrectOption(q.getCorrectOption());
|
list.add(qvo);
|
}
|
|
List<LiveSessionVO> list = new ArrayList<LiveSessionVO>();
|
int baseSort = (page - 1) * pageSize;
|
for (int i = 0; i < sessions.size(); i++) {
|
LiveSession s = sessions.get(i);
|
LiveSessionVO vo = new LiveSessionVO();
|
vo.setId(s.getId());
|
vo.setSort(baseSort + i + 1);
|
vo.setStartTime(s.getStartTime());
|
vo.setVideoUrl(s.getVideoUrl());
|
vo.setLiveStatus(s.getLiveStatus());
|
vo.setBaseReserveCount(s.getBaseReserveCount());
|
vo.setBaseWatchCount(s.getBaseWatchCount());
|
vo.setRealReserveCount(s.getRealReserveCount());
|
vo.setRealWatchCount(s.getRealWatchCount());
|
vo.setReserveConvertRate(s.getReserveConvertRate());
|
vo.setCompletionRate(s.getCompletionRate());
|
vo.setEffectiveWatchRate(s.getEffectiveWatchRate());
|
vo.setQuizCorrectCount(s.getQuizCorrectCount());
|
vo.setPointsIssued(s.getPointsIssued());
|
vo.setQuestions(map.get(s.getId()));
|
list.add(vo);
|
}
|
|
return PageResponse.of(list, p.getTotal(), p.getCurrent(), p.getSize());
|
}
|
|
@Transactional
|
public LiveSessionVO createSession(Long courseId, LiveSessionUpsertRequest req) {
|
requireCourse(courseId);
|
validateStartTime(req.getStartTime());
|
validateQuestions(req.getQuestions());
|
|
LiveSession s = new LiveSession();
|
s.setLiveCourseId(courseId);
|
s.setStartTime(req.getStartTime());
|
s.setVideoUrl(req.getVideoUrl());
|
s.setBaseReserveCount(safeInt(req.getBaseReserveCount()));
|
s.setBaseWatchCount(safeInt(req.getBaseWatchCount()));
|
s.setRealReserveCount(0);
|
s.setRealWatchCount(0);
|
s.setQuizCorrectCount(0);
|
s.setPointsIssued(0);
|
s.setLiveStatus(0);
|
liveSessionMapper.insert(s);
|
|
upsertQuestions(s.getId(), req.getQuestions());
|
|
return getSessionVo(s.getId(), 1);
|
}
|
|
@Transactional
|
public LiveSessionVO updateSession(Long courseId, Long sessionId, LiveSessionUpsertRequest req) {
|
LiveSession s = requireSession(courseId, sessionId);
|
if (s.getLiveStatus() != null && s.getLiveStatus() != 0) {
|
throw new IllegalArgumentException("仅允许修改未开始的场次");
|
}
|
validateStartTime(req.getStartTime());
|
validateQuestions(req.getQuestions());
|
|
s.setStartTime(req.getStartTime());
|
s.setVideoUrl(req.getVideoUrl());
|
s.setBaseReserveCount(safeInt(req.getBaseReserveCount()));
|
s.setBaseWatchCount(safeInt(req.getBaseWatchCount()));
|
liveSessionMapper.updateById(s);
|
|
liveSessionQuestionMapper.delete(new LambdaQueryWrapper<LiveSessionQuestion>()
|
.eq(LiveSessionQuestion::getSessionId, sessionId));
|
upsertQuestions(sessionId, req.getQuestions());
|
|
return getSessionVo(sessionId, 1);
|
}
|
|
@Transactional
|
public LiveSessionVO copySession(Long courseId, Long sessionId) {
|
LiveSession src = requireSession(courseId, sessionId);
|
|
LiveSession dst = new LiveSession();
|
dst.setLiveCourseId(courseId);
|
dst.setStartTime(null);
|
dst.setVideoUrl(null);
|
dst.setBaseReserveCount(safeInt(src.getBaseReserveCount()));
|
dst.setBaseWatchCount(safeInt(src.getBaseWatchCount()));
|
dst.setRealReserveCount(0);
|
dst.setRealWatchCount(0);
|
dst.setQuizCorrectCount(0);
|
dst.setPointsIssued(0);
|
dst.setLiveStatus(0);
|
liveSessionMapper.insert(dst);
|
|
List<LiveSessionQuestion> qs = liveSessionQuestionMapper.selectList(new LambdaQueryWrapper<LiveSessionQuestion>()
|
.eq(LiveSessionQuestion::getSessionId, sessionId)
|
.orderByAsc(LiveSessionQuestion::getSort));
|
if (qs != null && !qs.isEmpty()) {
|
for (LiveSessionQuestion q : qs) {
|
LiveSessionQuestion nq = new LiveSessionQuestion();
|
nq.setSessionId(dst.getId());
|
nq.setSort(q.getSort());
|
nq.setStem(q.getStem());
|
nq.setOptionA(q.getOptionA());
|
nq.setOptionB(q.getOptionB());
|
nq.setOptionC(q.getOptionC());
|
nq.setCorrectOption(q.getCorrectOption());
|
liveSessionQuestionMapper.insert(nq);
|
}
|
}
|
|
return getSessionVo(dst.getId(), 1);
|
}
|
|
@Transactional
|
public void deleteSession(Long courseId, Long sessionId) {
|
requireSession(courseId, sessionId);
|
liveSessionMapper.deleteById(sessionId);
|
}
|
|
@Transactional
|
public LiveSessionStatusVO start(Long courseId, Long sessionId) {
|
LiveSession s = requireSession(courseId, sessionId);
|
if (s.getLiveStatus() != null && s.getLiveStatus() != 0) {
|
throw new IllegalArgumentException("场次状态不允许开播");
|
}
|
s.setLiveStatus(1);
|
s.setActualStartTime(LocalDateTime.now());
|
liveSessionMapper.updateById(s);
|
return new LiveSessionStatusVO(s.getId(), s.getLiveStatus(), s.getActualStartTime(), null);
|
}
|
|
@Transactional
|
public LiveSessionStatusVO end(Long courseId, Long sessionId) {
|
LiveSession s = requireSession(courseId, sessionId);
|
if (s.getLiveStatus() == null || s.getLiveStatus() != 1) {
|
throw new IllegalArgumentException("场次状态不允许结束");
|
}
|
s.setLiveStatus(2);
|
s.setActualEndTime(LocalDateTime.now());
|
liveSessionMapper.updateById(s);
|
return new LiveSessionStatusVO(s.getId(), s.getLiveStatus(), null, s.getActualEndTime());
|
}
|
|
public LivePushNotificationVO pushNotification(Long courseId, Long sessionId) {
|
LiveSession session = requireSession(courseId, sessionId);
|
if (session.getLiveStatus() != null && session.getLiveStatus() != 0) {
|
throw new IllegalArgumentException("仅允许对未开始的场次推送开播提醒");
|
}
|
Long cnt = liveReserveMapper.selectCount(new LambdaQueryWrapper<LiveReserve>()
|
.eq(LiveReserve::getLiveCourseId, courseId)
|
.eq(LiveReserve::getSessionId, sessionId)
|
.eq(LiveReserve::getIsCancel, 0));
|
return new LivePushNotificationVO(cnt == null ? 0L : cnt, LocalDateTime.now());
|
}
|
|
private LiveCourse requireCourse(Long courseId) {
|
LiveCourse c = liveCourseMapper.selectById(courseId);
|
if (c == null) {
|
throw new IllegalArgumentException("课程不存在");
|
}
|
return c;
|
}
|
|
private LiveSession requireSession(Long courseId, Long sessionId) {
|
requireCourse(courseId);
|
LiveSession s = liveSessionMapper.selectById(sessionId);
|
if (s == null) {
|
throw new IllegalArgumentException("场次不存在");
|
}
|
if (!courseId.equals(s.getLiveCourseId())) {
|
throw new IllegalArgumentException("场次不属于该课程");
|
}
|
return s;
|
}
|
|
private LiveSessionVO getSessionVo(Long sessionId, int sort) {
|
LiveSession s = liveSessionMapper.selectById(sessionId);
|
if (s == null) {
|
throw new IllegalArgumentException("场次不存在");
|
}
|
|
List<LiveSessionQuestion> qs = liveSessionQuestionMapper.selectList(new LambdaQueryWrapper<LiveSessionQuestion>()
|
.eq(LiveSessionQuestion::getSessionId, sessionId)
|
.orderByAsc(LiveSessionQuestion::getSort));
|
List<LiveSessionQuestionVO> qvos = new ArrayList<LiveSessionQuestionVO>();
|
for (LiveSessionQuestion q : qs) {
|
LiveSessionQuestionVO qvo = new LiveSessionQuestionVO();
|
qvo.setId(q.getId());
|
qvo.setSort(q.getSort());
|
qvo.setStem(q.getStem());
|
qvo.setOptionA(q.getOptionA());
|
qvo.setOptionB(q.getOptionB());
|
qvo.setOptionC(q.getOptionC());
|
qvo.setCorrectOption(q.getCorrectOption());
|
qvos.add(qvo);
|
}
|
|
LiveSessionVO vo = new LiveSessionVO();
|
vo.setId(s.getId());
|
vo.setSort(sort);
|
vo.setStartTime(s.getStartTime());
|
vo.setVideoUrl(s.getVideoUrl());
|
vo.setLiveStatus(s.getLiveStatus());
|
vo.setBaseReserveCount(s.getBaseReserveCount());
|
vo.setBaseWatchCount(s.getBaseWatchCount());
|
vo.setRealReserveCount(s.getRealReserveCount());
|
vo.setRealWatchCount(s.getRealWatchCount());
|
vo.setReserveConvertRate(s.getReserveConvertRate());
|
vo.setCompletionRate(s.getCompletionRate());
|
vo.setEffectiveWatchRate(s.getEffectiveWatchRate());
|
vo.setQuizCorrectCount(s.getQuizCorrectCount());
|
vo.setPointsIssued(s.getPointsIssued());
|
vo.setQuestions(qvos);
|
return vo;
|
}
|
|
private void upsertQuestions(Long sessionId, List<LiveSessionQuestionRequest> questions) {
|
if (questions == null || questions.isEmpty()) {
|
return;
|
}
|
for (LiveSessionQuestionRequest q : questions) {
|
LiveSessionQuestion nq = new LiveSessionQuestion();
|
nq.setSessionId(sessionId);
|
nq.setSort(q.getSort());
|
nq.setStem(q.getStem());
|
nq.setOptionA(q.getOptionA());
|
nq.setOptionB(q.getOptionB());
|
nq.setOptionC(q.getOptionC());
|
nq.setCorrectOption(q.getCorrectOption());
|
liveSessionQuestionMapper.insert(nq);
|
}
|
}
|
|
private void validateQuestions(List<LiveSessionQuestionRequest> questions) {
|
if (questions == null || questions.isEmpty()) {
|
throw new IllegalArgumentException("题目数量必须为3道");
|
}
|
if (questions.size() != 3) {
|
throw new IllegalArgumentException("题目数量必须为3道");
|
}
|
Set<Integer> sortSet = new HashSet<Integer>();
|
for (LiveSessionQuestionRequest q : questions) {
|
if (q == null || q.getSort() == null) {
|
continue;
|
}
|
if (!sortSet.add(q.getSort())) {
|
throw new IllegalArgumentException("题目序号重复");
|
}
|
}
|
}
|
|
private void validateStartTime(LocalDateTime startTime) {
|
if (startTime == null) {
|
return;
|
}
|
if (startTime.isBefore(LocalDateTime.now())) {
|
throw new IllegalArgumentException("开始直播时间不能早于当前时间");
|
}
|
}
|
|
private int safeInt(Integer v) {
|
return v == null ? 0 : v;
|
}
|
}
|