zjk
2026-09-29 54dcf0a82e7490fa58b1c056d5c0b9cb739c2931
tcm-training-admin-backend/src/test/java/com/tcm/training/admin/app/live/service/AppLiveCourseServiceTest.java
@@ -3,17 +3,27 @@
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.tcm.training.admin.app.auth.service.AppAuthSessionService;
import com.tcm.training.admin.app.live.dto.AppLiveQuizAnswerRequest;
import com.tcm.training.admin.app.live.dto.AppLiveQuizSubmitRequest;
import com.tcm.training.admin.app.live.support.AppLiveCourseDisplaySupport;
import com.tcm.training.admin.app.live.vo.AppLiveCourseDetailVO;
import com.tcm.training.admin.app.live.vo.AppLiveQuizSubmitResultVO;
import com.tcm.training.admin.app.live.vo.AppLiveReserveResultVO;
import com.tcm.training.admin.live.entity.Expert;
import com.tcm.training.admin.live.entity.LiveCourse;
import com.tcm.training.admin.live.entity.LiveReserve;
import com.tcm.training.admin.live.entity.LiveSession;
import com.tcm.training.admin.live.entity.LiveSessionQuestion;
import com.tcm.training.admin.live.entity.LiveSessionQuizRecord;
import com.tcm.training.admin.live.mapper.ExpertMapper;
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.mapper.LiveSessionQuizRecordMapper;
import com.tcm.training.admin.live.mapper.LiveWatchRecordMapper;
import com.tcm.training.admin.points.mapper.UserPointsAccountMapper;
import com.tcm.training.admin.points.service.PointsLedgerAppService;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
@@ -27,11 +37,15 @@
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -63,6 +77,21 @@
    private ExpertMapper expertMapper;
    @Mock
    private LiveSessionQuestionMapper liveSessionQuestionMapper;
    @Mock
    private LiveSessionQuizRecordMapper liveSessionQuizRecordMapper;
    @Mock
    private LiveWatchRecordMapper liveWatchRecordMapper;
    @Mock
    private UserPointsAccountMapper userPointsAccountMapper;
    @Mock
    private PointsLedgerAppService pointsLedgerAppService;
    @Mock
    private AppAuthSessionService appAuthSessionService;
    private AppLiveCourseService appLiveCourseService;
@@ -76,11 +105,20 @@
    @BeforeEach
    void setUp() {
        AppLiveExpertService appLiveExpertService = new AppLiveExpertService(expertMapper);
        // 详情与预约用例不关心答题和观看明细,统一返回空列表避免空指针干扰主断言。
        lenient().when(liveSessionQuestionMapper.selectList(any())).thenReturn(Collections.emptyList());
        lenient().when(liveSessionQuizRecordMapper.selectList(any())).thenReturn(Collections.emptyList());
        lenient().when(liveWatchRecordMapper.selectList(any())).thenReturn(Collections.emptyList());
        appLiveCourseService = new AppLiveCourseService(
                liveCourseMapper,
                liveSessionMapper,
                liveReserveMapper,
                expertMapper,
                liveSessionQuestionMapper,
                liveSessionQuizRecordMapper,
                liveWatchRecordMapper,
                userPointsAccountMapper,
                pointsLedgerAppService,
                appAuthSessionService,
                new AppLiveCourseDisplaySupport(),
                appLiveExpertService
@@ -145,7 +183,7 @@
        assertEquals("立即查看", detailVO.getActionText());
        assertEquals(Long.valueOf(201L), detailVO.getActionSessionId());
        assertNull(detailVO.getCountdownTime());
        assertEquals("https://cdn.example/live.m3u8", detailVO.getVideoUrl());
        assertNull(detailVO.getVideoUrl());
    }
    /**
@@ -173,7 +211,39 @@
        assertEquals("ENDED", detailVO.getDisplayStatus());
        assertEquals("WATCHABLE", detailVO.getActionStatus());
        assertEquals(Long.valueOf(211L), detailVO.getActionSessionId());
        assertEquals("https://cdn.example/latest-ended.m3u8", detailVO.getVideoUrl());
        assertNull(detailVO.getVideoUrl());
    }
    /**
     * 同一直播课同一答题自然日已经获得过积分时,后续场次答对也不重复发放。
     */
    @Test
    void submitQuizShouldNotAwardPointsTwiceForSameCourseSameDay() {
        LiveCourse course = buildCourse(50L, 12L);
        LiveSession session = buildSession(501L, 50L, LocalDateTime.now().minusMinutes(30),
                AppLiveCourseDisplaySupport.SESSION_STATUS_LIVING);
        List<LiveSessionQuestion> questions = Arrays.asList(
                buildQuestion(1L, 501L, 1, "A"),
                buildQuestion(2L, 501L, 2, "B"),
                buildQuestion(3L, 501L, 3, "C")
        );
        when(appAuthSessionService.requireLoginUserId()).thenReturn(77L);
        when(liveCourseMapper.selectOne(any())).thenReturn(course);
        when(liveSessionMapper.selectOne(any())).thenReturn(session);
        when(liveSessionQuestionMapper.selectList(any())).thenReturn(questions);
        when(liveSessionQuizRecordMapper.selectList(any())).thenReturn(Collections.emptyList());
        when(liveSessionQuizRecordMapper.countAwardedCourseQuizPoints(anyLong(), anyLong(), any(), any())).thenReturn(1);
        AppLiveQuizSubmitResultVO resultVO = appLiveCourseService.submitQuiz(50L, 501L, buildQuizSubmitRequest());
        verify(liveSessionQuizRecordMapper, never()).updateById(any(LiveSessionQuizRecord.class));
        verify(userPointsAccountMapper, never()).update(any(), any());
        verify(pointsLedgerAppService, never()).syncLedger();
        assertEquals(Boolean.TRUE, resultVO.getSubmitted());
        assertEquals(Boolean.TRUE, resultVO.getAllCorrect());
        assertEquals(Boolean.FALSE, resultVO.getPointsAwarded());
        assertEquals("今日该课程积分已获得", resultVO.getMessage());
    }
    /**
@@ -262,6 +332,45 @@
    }
    /**
     * 构造直播答题题目测试数据。
     */
    private LiveSessionQuestion buildQuestion(Long questionId, Long sessionId, Integer sort, String correctOption) {
        LiveSessionQuestion question = new LiveSessionQuestion();
        question.setId(questionId);
        question.setSessionId(sessionId);
        question.setSort(sort);
        question.setStem("题目" + sort);
        question.setOptionA("选项A");
        question.setOptionB("选项B");
        question.setOptionC("选项C");
        question.setCorrectOption(correctOption);
        return question;
    }
    /**
     * 构造三题全对的答题提交请求。
     */
    private AppLiveQuizSubmitRequest buildQuizSubmitRequest() {
        AppLiveQuizSubmitRequest request = new AppLiveQuizSubmitRequest();
        request.setAnswers(Arrays.asList(
                buildQuizAnswer(1L, "A"),
                buildQuizAnswer(2L, "B"),
                buildQuizAnswer(3L, "C")
        ));
        return request;
    }
    /**
     * 构造单题答题请求。
     */
    private AppLiveQuizAnswerRequest buildQuizAnswer(Long questionId, String chosenOption) {
        AppLiveQuizAnswerRequest answer = new AppLiveQuizAnswerRequest();
        answer.setQuestionId(questionId);
        answer.setChosenOption(chosenOption);
        return answer;
    }
    /**
     * 构造启用中的专家测试数据。
     */
    private Expert buildExpert(Long expertId) {