|
|
@@ -7,8 +7,11 @@ import com.fs.common.exception.base.BaseException;
|
|
|
import com.fs.common.utils.StringUtils;
|
|
|
import com.fs.live.domain.*;
|
|
|
import com.fs.live.mapper.LiveQuestionBankMapper;
|
|
|
+import com.fs.live.param.LiveCompletionCouponAnswerItem;
|
|
|
import com.fs.live.param.LiveCompletionCouponAnswerParam;
|
|
|
+import com.fs.live.param.LiveCompletionCouponClaimParam;
|
|
|
import com.fs.live.service.*;
|
|
|
+import com.fs.live.vo.LiveCompletionCouponAnswerResult;
|
|
|
import com.fs.live.vo.LiveCompletionCouponConfigVO;
|
|
|
import com.fs.live.vo.LiveCompletionCouponInfoVO;
|
|
|
import com.fs.live.vo.LiveCompletionCouponNotifyResult;
|
|
|
@@ -40,6 +43,7 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
|
|
|
private static final String COMPLETION_COUPON_TYPE = "4";
|
|
|
|
|
|
private static final String NOTIFY_REDIS_KEY_PREFIX = "live:completion:coupon:notify:";
|
|
|
+ private static final String ANSWER_RECORD_REDIS_KEY_PREFIX = "live:completion:coupon:answer:record:";
|
|
|
|
|
|
@Autowired
|
|
|
private ILiveService liveService;
|
|
|
@@ -65,6 +69,9 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
|
|
|
@Autowired
|
|
|
private RedisCache redisCache;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private ILiveConsoleOpLogService liveConsoleOpLogService;
|
|
|
+
|
|
|
@Override
|
|
|
public LiveCompletionCouponConfigVO parseCompletionCouponConfig(Live live) {
|
|
|
LiveCompletionCouponConfigVO vo = new LiveCompletionCouponConfigVO();
|
|
|
@@ -163,6 +170,9 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
|
|
|
status.setQuestions(questions);
|
|
|
status.setEligible(isWatchRateEligible(liveId, userId, watchDuration, config));
|
|
|
status.setReceivedToday(hasIssuedToday(liveId, userId, config.getCouponId()));
|
|
|
+ AnswerRecord answerRecord = getAnswerRecordToday(liveId, userId);
|
|
|
+ status.setAnsweredToday(answerRecord != null);
|
|
|
+ status.setAllCorrect(answerRecord != null && answerRecord.isAllCorrect());
|
|
|
return status;
|
|
|
}
|
|
|
|
|
|
@@ -177,11 +187,11 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
|
|
|
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public void submitAnswerAndIssueCoupon(LiveCompletionCouponAnswerParam param, Long userId) {
|
|
|
+ public LiveCompletionCouponAnswerResult submitAnswers(LiveCompletionCouponAnswerParam param, Long userId) {
|
|
|
if (param == null || param.getLiveId() == null) {
|
|
|
throw new BaseException("参数错误");
|
|
|
}
|
|
|
- if (param.getQuestions() == null || param.getQuestions().isEmpty()) {
|
|
|
+ if (param.getAnswers() == null || param.getAnswers().isEmpty()) {
|
|
|
throw new BaseException("请完成今日问题");
|
|
|
}
|
|
|
|
|
|
@@ -190,11 +200,9 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
|
|
|
if (!config.isEnabled() || config.getCouponId() == null) {
|
|
|
throw new BaseException("完课优惠券未开启");
|
|
|
}
|
|
|
-
|
|
|
if (!isWatchRateEligible(liveId, userId, null, config)) {
|
|
|
throw new BaseException("未达到完课要求");
|
|
|
}
|
|
|
-
|
|
|
if (hasIssuedToday(liveId, userId, config.getCouponId())) {
|
|
|
throw new BaseException("今日福利券已领取");
|
|
|
}
|
|
|
@@ -204,18 +212,55 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
|
|
|
throw new BaseException("未配置今日问题");
|
|
|
}
|
|
|
|
|
|
- validateAnswers(param.getQuestions(), configuredQuestionIds);
|
|
|
+ boolean allCorrect = evaluateAnswers(param.getAnswers(), configuredQuestionIds);
|
|
|
+ saveAnswerRecordToday(liveId, userId, allCorrect);
|
|
|
+
|
|
|
+ LiveCompletionCouponAnswerResult result = new LiveCompletionCouponAnswerResult();
|
|
|
+ result.setAllCorrect(allCorrect);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public LiveCouponUser claimCompletionCoupon(LiveCompletionCouponClaimParam param, Long userId) {
|
|
|
+ if (param == null || param.getLiveId() == null) {
|
|
|
+ throw new BaseException("参数错误");
|
|
|
+ }
|
|
|
+ Long liveId = param.getLiveId();
|
|
|
+ CompletionCouponConfig config = resolveConfig(liveId);
|
|
|
+ if (!config.isEnabled() || config.getCouponId() == null) {
|
|
|
+ throw new BaseException("完课优惠券未开启");
|
|
|
+ }
|
|
|
+ if (!isWatchRateEligible(liveId, userId, null, config)) {
|
|
|
+ throw new BaseException("未达到完课要求");
|
|
|
+ }
|
|
|
+ if (hasIssuedToday(liveId, userId, config.getCouponId())) {
|
|
|
+ throw new BaseException("今日福利券已领取");
|
|
|
+ }
|
|
|
+ AnswerRecord answerRecord = getAnswerRecordToday(liveId, userId);
|
|
|
+ if (answerRecord == null) {
|
|
|
+ throw new BaseException("请先完成今日问题");
|
|
|
+ }
|
|
|
+ if (!answerRecord.isAllCorrect()) {
|
|
|
+ throw new BaseException("回答错误,请重新作答后再领取");
|
|
|
+ }
|
|
|
|
|
|
Live live = liveService.selectLiveByLiveId(liveId);
|
|
|
if (live == null) {
|
|
|
throw new BaseException("直播不存在");
|
|
|
}
|
|
|
- issueCoupon(live, userId, config.getCouponId());
|
|
|
+ LiveCouponUser couponUser = issueCoupon(live, userId, config.getCouponId());
|
|
|
+ liveConsoleOpLogService.bindOpLogUser(
|
|
|
+ param.getOpLogId(), liveId, userId, couponUser.getId());
|
|
|
+ return couponUser;
|
|
|
}
|
|
|
|
|
|
- private void validateAnswers(List<LiveQuestionBank> userAnswers, List<Long> configuredQuestionIds) {
|
|
|
+ /**
|
|
|
+ * 校验是否答完全部题目,并返回是否全部答对(答错不阻断记录)
|
|
|
+ */
|
|
|
+ private boolean evaluateAnswers(List<LiveCompletionCouponAnswerItem> userAnswers, List<Long> configuredQuestionIds) {
|
|
|
Set<Long> submittedIds = userAnswers.stream()
|
|
|
- .map(LiveQuestionBank::getId)
|
|
|
+ .map(LiveCompletionCouponAnswerItem::getQuestionId)
|
|
|
.filter(Objects::nonNull)
|
|
|
.collect(Collectors.toSet());
|
|
|
if (submittedIds.size() != configuredQuestionIds.size()
|
|
|
@@ -228,28 +273,44 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
|
|
|
.stream()
|
|
|
.collect(Collectors.toMap(LiveQuestionBank::getId, q -> q));
|
|
|
|
|
|
- for (LiveQuestionBank userAnswer : userAnswers) {
|
|
|
- LiveQuestionBank correctAnswer = correctAnswersMap.get(userAnswer.getId());
|
|
|
+ boolean allCorrect = true;
|
|
|
+ for (LiveCompletionCouponAnswerItem userAnswer : userAnswers) {
|
|
|
+ LiveQuestionBank correctAnswer = correctAnswersMap.get(userAnswer.getQuestionId());
|
|
|
if (correctAnswer == null || correctAnswer.getStatus() == null || correctAnswer.getStatus() == 0) {
|
|
|
throw new BaseException("题目不存在或已停用");
|
|
|
}
|
|
|
- if (!isAnswerCorrect(userAnswer, correctAnswer)) {
|
|
|
- throw new BaseException("回答错误,请重新作答");
|
|
|
+ if (!isAnswerCorrect(userAnswer.getAnswer(), correctAnswer)) {
|
|
|
+ allCorrect = false;
|
|
|
}
|
|
|
}
|
|
|
+ return allCorrect;
|
|
|
}
|
|
|
|
|
|
- private boolean isAnswerCorrect(LiveQuestionBank userAnswer, LiveQuestionBank correctAnswer) {
|
|
|
+ private boolean isAnswerCorrect(String userAnswerValue, LiveQuestionBank correctAnswer) {
|
|
|
if (correctAnswer.getType() == null || correctAnswer.getType() == 1L) {
|
|
|
- return Objects.equals(userAnswer.getAnswer(), correctAnswer.getAnswer());
|
|
|
+ return Objects.equals(normalizeSingleAnswer(userAnswerValue), normalizeSingleAnswer(correctAnswer.getAnswer()));
|
|
|
}
|
|
|
- String[] userAnswers = parseAnswerArray(userAnswer.getAnswer());
|
|
|
+ String[] userAnswers = parseAnswerArray(userAnswerValue);
|
|
|
String[] correctAnswers = parseAnswerArray(correctAnswer.getAnswer());
|
|
|
Arrays.sort(userAnswers);
|
|
|
Arrays.sort(correctAnswers);
|
|
|
return Arrays.equals(userAnswers, correctAnswers);
|
|
|
}
|
|
|
|
|
|
+ private String normalizeSingleAnswer(String answer) {
|
|
|
+ if (StringUtils.isEmpty(answer)) {
|
|
|
+ return answer;
|
|
|
+ }
|
|
|
+ String trimmed = answer.trim();
|
|
|
+ if (trimmed.startsWith("[")) {
|
|
|
+ List<String> list = JSON.parseArray(trimmed, String.class);
|
|
|
+ if (list != null && !list.isEmpty()) {
|
|
|
+ return list.get(0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return trimmed;
|
|
|
+ }
|
|
|
+
|
|
|
private String[] parseAnswerArray(String answer) {
|
|
|
if (StringUtils.isEmpty(answer)) {
|
|
|
return new String[0];
|
|
|
@@ -298,7 +359,7 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
|
|
|
return getCompletionCouponConfig(live);
|
|
|
}
|
|
|
|
|
|
- private void issueCoupon(Live live, Long userId, Long couponId) {
|
|
|
+ private LiveCouponUser issueCoupon(Live live, Long userId, Long couponId) {
|
|
|
LiveCoupon coupon = liveCouponService.selectLiveCouponById(couponId);
|
|
|
if (coupon == null) {
|
|
|
throw new BaseException("优惠券不存在");
|
|
|
@@ -346,6 +407,7 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
|
|
|
liveRewardRecordService.insertLiveRewardRecord(record);
|
|
|
|
|
|
log.info("完课优惠券发放成功, liveId={}, userId={}, couponId={}", live.getLiveId(), userId, couponId);
|
|
|
+ return couponUser;
|
|
|
}
|
|
|
|
|
|
private boolean hasIssuedToday(Long liveId, Long userId, Long couponId) {
|
|
|
@@ -478,6 +540,28 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
|
|
|
redisCache.setCacheObject(buildNotifyRedisKey(liveId, userId), Boolean.TRUE, 1, TimeUnit.DAYS);
|
|
|
}
|
|
|
|
|
|
+ private void saveAnswerRecordToday(Long liveId, Long userId, boolean allCorrect) {
|
|
|
+ AnswerRecord record = new AnswerRecord();
|
|
|
+ record.setAllCorrect(allCorrect);
|
|
|
+ redisCache.setCacheObject(buildAnswerRecordRedisKey(liveId, userId), record, 1, TimeUnit.DAYS);
|
|
|
+ }
|
|
|
+
|
|
|
+ private AnswerRecord getAnswerRecordToday(Long liveId, Long userId) {
|
|
|
+ Object cache = redisCache.getCacheObject(buildAnswerRecordRedisKey(liveId, userId));
|
|
|
+ if (cache == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (cache instanceof AnswerRecord) {
|
|
|
+ return (AnswerRecord) cache;
|
|
|
+ }
|
|
|
+ return JSON.parseObject(JSON.toJSONString(cache), AnswerRecord.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String buildAnswerRecordRedisKey(Long liveId, Long userId) {
|
|
|
+ String today = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
|
|
|
+ return ANSWER_RECORD_REDIS_KEY_PREFIX + liveId + ":" + userId + ":" + today;
|
|
|
+ }
|
|
|
+
|
|
|
private String buildNotifyRedisKey(Long liveId, Long userId) {
|
|
|
String today = LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE);
|
|
|
return NOTIFY_REDIS_KEY_PREFIX + liveId + ":" + userId + ":" + today;
|
|
|
@@ -526,4 +610,16 @@ public class LiveCompletionCouponServiceImpl implements ILiveCompletionCouponSer
|
|
|
this.finishQuestionIds = finishQuestionIds;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ private static class AnswerRecord {
|
|
|
+ private boolean allCorrect;
|
|
|
+
|
|
|
+ public boolean isAllCorrect() {
|
|
|
+ return allCorrect;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setAllCorrect(boolean allCorrect) {
|
|
|
+ this.allCorrect = allCorrect;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|