| | |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | |
| | | detailVO.setDisplayStatusText(resolveDisplayStatusText(snapshot)); |
| | | detailVO.setCountdownTime(resolveCountdownTime(snapshot)); |
| | | detailVO.setActionSessionId(actionSession == null ? null : actionSession.getId()); |
| | | detailVO.setVideoUrl(actionSession == null ? null : appLiveCourseDisplaySupport.trimToNull(actionSession.getVideoUrl())); |
| | | detailVO.setVideoUrl(resolvePlayableVideoUrl(snapshot, actionSession)); |
| | | detailVO.setPlaybackStartTime(playbackStartTime); |
| | | detailVO.setDurationSeconds(actionSession == null ? null : actionSession.getDurationSeconds()); |
| | | detailVO.setReserveCount(resolveReserveCount(courseId, sessions)); |
| | |
| | | |
| | | Map<Long, String> normalizedAnswers = validateAndNormalizeAnswers(request.getAnswers(), questions); |
| | | WatchProgressSummary watchSummary = resolveWatchProgress(sessionId, userId, session); |
| | | LocalDateTime answeredAt = LocalDateTime.now(); |
| | | boolean allCorrect = true; |
| | | List<LiveSessionQuizRecord> insertedRecords = new ArrayList<LiveSessionQuizRecord>(); |
| | | for (LiveSessionQuestion question : questions) { |
| | |
| | | record.setChosenOption(chosenOption); |
| | | record.setIsCorrect(correct ? 1 : 0); |
| | | record.setPointsEarned(0); |
| | | record.setAnsweredAt(LocalDateTime.now()); |
| | | record.setAnsweredAt(answeredAt); |
| | | liveSessionQuizRecordMapper.insert(record); |
| | | insertedRecords.add(record); |
| | | } |
| | | |
| | | boolean pointsAwarded = false; |
| | | boolean dailyCoursePointsAlreadyAwarded = false; |
| | | // 当前业务改为只要三题全部答对就立即发放积分,不再依赖完整观看直播。 |
| | | if (allCorrect) { |
| | | pointsAwarded = awardQuizPoints(course, session, userId, insertedRecords, questions); |
| | | dailyCoursePointsAlreadyAwarded = hasAwardedCourseQuizPointsToday(course.getId(), userId, answeredAt); |
| | | pointsAwarded = awardQuizPoints(course, session, userId, insertedRecords, questions, answeredAt); |
| | | } |
| | | |
| | | AppLiveQuizSubmitResultVO resultVO = new AppLiveQuizSubmitResultVO(); |
| | |
| | | resultVO.setWatchCompleted(Boolean.valueOf(watchSummary.isWatchCompleted())); |
| | | resultVO.setPointsAwarded(Boolean.valueOf(pointsAwarded)); |
| | | resultVO.setPointsReward(Integer.valueOf(defaultNumber(course.getQuizPoints()))); |
| | | resultVO.setMessage(resolveQuizSubmitMessage(allCorrect, pointsAwarded)); |
| | | resultVO.setMessage(resolveQuizSubmitMessage(allCorrect, pointsAwarded, dailyCoursePointsAlreadyAwarded)); |
| | | return resultVO; |
| | | } |
| | | |
| | |
| | | LiveSession session, |
| | | Long userId, |
| | | List<LiveSessionQuizRecord> quizRecords, |
| | | List<LiveSessionQuestion> questions) { |
| | | List<LiveSessionQuestion> questions, |
| | | LocalDateTime answeredAt) { |
| | | int pointsReward = defaultNumber(course.getQuizPoints()); |
| | | if (pointsReward <= 0 || hasAwardedQuizPoints(quizRecords)) { |
| | | return false; |
| | | } |
| | | if (hasAwardedCourseQuizPointsToday(course.getId(), userId, answeredAt)) { |
| | | return false; |
| | | } |
| | | LiveSessionQuizRecord rewardRecord = resolveRewardRecord(quizRecords, questions); |
| | |
| | | // 积分到账后立即同步流水,避免 APP 积分页与直播观看页状态延迟。 |
| | | pointsLedgerAppService.syncLedger(); |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 判断用户在同一直播课的答题自然日内是否已获得过积分奖励。 |
| | | */ |
| | | private boolean hasAwardedCourseQuizPointsToday(Long courseId, Long userId, LocalDateTime answeredAt) { |
| | | if (courseId == null || userId == null) { |
| | | return false; |
| | | } |
| | | LocalDate answerDate = (answeredAt == null ? LocalDateTime.now() : answeredAt).toLocalDate(); |
| | | Integer awardedCount = liveSessionQuizRecordMapper.countAwardedCourseQuizPoints( |
| | | courseId, |
| | | userId, |
| | | answerDate.atStartOfDay(), |
| | | answerDate.plusDays(1).atStartOfDay() |
| | | ); |
| | | return awardedCount != null && awardedCount.intValue() > 0; |
| | | } |
| | | |
| | | /** |
| | |
| | | /** |
| | | * 构造答题提交后的提示文案。 |
| | | */ |
| | | private String resolveQuizSubmitMessage(boolean allCorrect, boolean pointsAwarded) { |
| | | private String resolveQuizSubmitMessage(boolean allCorrect, |
| | | boolean pointsAwarded, |
| | | boolean dailyCoursePointsAlreadyAwarded) { |
| | | if (!allCorrect) { |
| | | return "回答错误,本次不获得积分"; |
| | | } |
| | | if (pointsAwarded) { |
| | | return "答题成功,积分已到账"; |
| | | } |
| | | if (dailyCoursePointsAlreadyAwarded) { |
| | | return "今日该课程积分已获得"; |
| | | } |
| | | return "答题成功"; |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * 解析观看页可播放视频地址,直播结束冷却期只保留答题入口不继续播放视频。 |
| | | */ |
| | | private String resolvePlayableVideoUrl(AppLiveCourseDisplaySupport.CourseDisplaySnapshot snapshot, |
| | | LiveSession actionSession) { |
| | | if (snapshot.isEndedCooldown() || actionSession == null) { |
| | | return null; |
| | | } |
| | | return appLiveCourseDisplaySupport.trimToNull(actionSession.getVideoUrl()); |
| | | } |
| | | |
| | | /** |
| | | * 解析模拟直播回放进度的起播时间,优先使用实际开播时间。 |
| | | */ |
| | | private LocalDateTime resolvePlaybackStartTime(LiveSession actionSession) { |