|
|
@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSON;
|
|
|
import com.alibaba.fastjson.JSONArray;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import com.fs.common.core.domain.R;
|
|
|
+import com.fs.common.core.redis.RedisCache;
|
|
|
import com.fs.common.exception.ServiceException;
|
|
|
import com.fs.common.utils.DateUtils;
|
|
|
import com.fs.common.utils.StringUtils;
|
|
|
@@ -13,14 +14,20 @@ import com.fs.course.domain.*;
|
|
|
import com.fs.course.dto.FsCourseQuestionBankImportDTO;
|
|
|
import com.fs.course.dto.ImportResultDTO;
|
|
|
import com.fs.course.mapper.*;
|
|
|
+import com.fs.course.param.FsCourseAnswerStatusQueryParam;
|
|
|
import com.fs.course.param.FsCourseQuestionAnswerUParam;
|
|
|
import com.fs.course.param.LiveQuizSubmitUParam;
|
|
|
import com.fs.course.service.IFsCourseQuestionBankService;
|
|
|
+import com.fs.course.vo.FsCourseAnswerStatusVO;
|
|
|
import com.fs.course.util.CourseConfigUserAnswerExpose;
|
|
|
import com.fs.live.mapper.LiveCourseQuestionRelMapper;
|
|
|
import com.fs.his.domain.FsUser;
|
|
|
+import com.fs.his.domain.FsUserIntegralLogs;
|
|
|
+import com.fs.his.enums.FsUserIntegralLogTypeEnum;
|
|
|
+import com.fs.his.mapper.FsUserIntegralLogsMapper;
|
|
|
import com.fs.his.mapper.FsUserMapper;
|
|
|
import com.fs.his.service.IFsStorePaymentService;
|
|
|
+import com.fs.his.service.IFsUserIntegralLogsService;
|
|
|
import com.fs.system.service.ISysConfigService;
|
|
|
import lombok.Getter;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
@@ -28,6 +35,7 @@ import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
import java.util.*;
|
|
|
import java.util.function.BiConsumer;
|
|
|
import java.util.function.Function;
|
|
|
@@ -64,8 +72,25 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
|
|
|
private LiveCourseQuestionRelMapper liveCourseQuestionRelMapper;
|
|
|
@Autowired
|
|
|
private FsUserCourseCategoryMapper courseCategoryMapper;
|
|
|
+ @Autowired
|
|
|
+ private FsUserCourseStudyLogMapper fsUserCourseStudyLogMapper;
|
|
|
+ @Autowired
|
|
|
+ private FsUserIntegralLogsMapper fsUserIntegralLogsMapper;
|
|
|
+ @Autowired
|
|
|
+ private IFsUserIntegralLogsService fsUserIntegralLogsService;
|
|
|
+ @Autowired
|
|
|
+ private RedisCache redisCache;
|
|
|
@Value("${cloud_host.company_name}")
|
|
|
private String signProjectName;
|
|
|
+
|
|
|
+ /** 用户端(APP)答题:允许答错的上限次数(与 course.config 无关,固定写死) */
|
|
|
+ private static final int APP_USER_ANSWER_MAX_ATTEMPTS = 3;
|
|
|
+
|
|
|
+ private static final String REDIS_LOCK_APP_COURSE_ANSWER = "course:answer:userApp:lock:";
|
|
|
+
|
|
|
+ private static final String REDIS_LOCK_APP_COURSE_ANSWER_STATUS = "course:answer:userApp:status:lock:";
|
|
|
+
|
|
|
+ private static final String REDIS_LOCK_APP_COURSE_ANSWER_CLAIM = "course:answer:userApp:claim:lock:";
|
|
|
/**
|
|
|
* 查询题库
|
|
|
*
|
|
|
@@ -558,6 +583,343 @@ public class FsCourseQuestionBankServiceImpl implements IFsCourseQuestionBankSer
|
|
|
return R.ok("回答错误").put("correct", false);
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public R courseAnswerForUserApp(FsCourseQuestionAnswerUParam param) {
|
|
|
+ if (param == null || param.getUserId() == null || param.getVideoId() == null) {
|
|
|
+ return R.error("参数不完整");
|
|
|
+ }
|
|
|
+ String lockKey = REDIS_LOCK_APP_COURSE_ANSWER + param.getUserId() + ":" + param.getVideoId()
|
|
|
+ + (param.getPeriodId() != null ? ":" + param.getPeriodId() : "");
|
|
|
+ boolean locked = redisCache.setIfAbsent(lockKey, "1", 45L, TimeUnit.SECONDS);
|
|
|
+ if (!locked) {
|
|
|
+ return R.error("操作过于频繁,请稍后重试");
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return courseAnswerForUserAppLocked(param);
|
|
|
+ } finally {
|
|
|
+ redisCache.deleteObject(lockKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 历史上是否曾有答对记录(营期 / 短链维度与答题接口一致)。
|
|
|
+ */
|
|
|
+ private FsCourseAnswerLogs selectEverSucceededAnswerLogForApp(Long videoId, Long userId, String qwUserId, Long periodId, Integer linkType) {
|
|
|
+ if (linkType != null && linkType == 1) {
|
|
|
+ return courseAnswerLogsMapper.selectRightLogByCourseVideo(videoId, userId, null);
|
|
|
+ }
|
|
|
+ if (periodId != null) {
|
|
|
+ return courseAnswerLogsMapper.selectRightLogByCourseVideoWithPeriodId(videoId, userId, qwUserId, periodId);
|
|
|
+ }
|
|
|
+ return courseAnswerLogsMapper.selectRightLogByCourseVideo(videoId, userId, qwUserId);
|
|
|
+ }
|
|
|
+
|
|
|
+ private FsCourseWatchLog resolveCourseWatchLogForApp(Long videoId, Long userId, Long companyUserId, Long periodId) {
|
|
|
+ if (periodId != null) {
|
|
|
+ return courseWatchLogMapper.getWatchLogByFsUserAndPeriodId(videoId, userId, companyUserId, periodId);
|
|
|
+ }
|
|
|
+ return courseWatchLogMapper.getWatchLogByFsUser(videoId, userId, companyUserId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用户端答题核心逻辑(已由外层 Redis 唯一锁串行)。依赖 {@link FsUserCourseServiceImpl#addStudyCourse} 写入的学习小节记录。
|
|
|
+ */
|
|
|
+ private R courseAnswerForUserAppLocked(FsCourseQuestionAnswerUParam param) {
|
|
|
+ FsCourseAnswerLogs everRight = selectEverSucceededAnswerLogForApp(
|
|
|
+ param.getVideoId(), param.getUserId(), param.getQwUserId(), param.getPeriodId(), param.getLinkType());
|
|
|
+ if (everRight != null) {
|
|
|
+ return R.ok("你已完成过当前课程的答题");
|
|
|
+ }
|
|
|
+
|
|
|
+ FsUserCourseStudyLog studyRow = fsUserCourseStudyLogMapper.selectFsUserCourseStudyLogByVideoToday(param.getUserId(), param.getVideoId());
|
|
|
+ if (studyRow == null) {
|
|
|
+ return R.error("检测学习数据丢失,请联系管理处理");
|
|
|
+ }
|
|
|
+
|
|
|
+ int thisRightCount = 0;
|
|
|
+ int errorCount;
|
|
|
+ List<FsCourseQuestionBank> incorrectQuestions = new ArrayList<>();
|
|
|
+ Long logId = null;
|
|
|
+
|
|
|
+ if (param.getLinkType() != null && param.getLinkType() == 1) {
|
|
|
+ errorCount = courseAnswerLogsMapper.selectErrorCountByCourseVideoToday(param.getVideoId(), param.getUserId(), null, null);
|
|
|
+ } else {
|
|
|
+ FsCourseWatchLog courseWatchLog = resolveCourseWatchLogForApp(
|
|
|
+ param.getVideoId(), param.getUserId(), param.getCompanyUserId(), param.getPeriodId());
|
|
|
+ if (courseWatchLog == null) {
|
|
|
+ return R.error("无看课记录");
|
|
|
+ }
|
|
|
+ if (courseWatchLog.getLogType() == null || courseWatchLog.getLogType() != 2) {
|
|
|
+ return R.error("未完课");
|
|
|
+ }
|
|
|
+ logId = courseWatchLog.getLogId();
|
|
|
+ errorCount = courseAnswerLogsMapper.selectErrorCountByCourseVideoToday(
|
|
|
+ param.getVideoId(), param.getUserId(), param.getQwUserId(), courseWatchLog.getProject());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (errorCount >= APP_USER_ANSWER_MAX_ATTEMPTS) {
|
|
|
+ return R.error("已达答题次数上限,最多可尝试" + APP_USER_ANSWER_MAX_ATTEMPTS + "次");
|
|
|
+ }
|
|
|
+ int remainCount = APP_USER_ANSWER_MAX_ATTEMPTS - errorCount - 1;
|
|
|
+
|
|
|
+ List<FsCourseQuestionBank> questions = param.getQuestions();
|
|
|
+ if (questions != null && !questions.isEmpty()) {
|
|
|
+// if (skipAnswerValidation) {
|
|
|
+// thisRightCount = questions.size();
|
|
|
+// } else {
|
|
|
+ Map<Long, FsCourseQuestionBank> correctAnswersMap = fsCourseQuestionBankMapper.selectFsCourseQuestionBankByIds(
|
|
|
+ questions.stream().map(FsCourseQuestionBank::getId).collect(Collectors.toList())
|
|
|
+ ).stream().collect(Collectors.toMap(FsCourseQuestionBank::getId, question -> question));
|
|
|
+
|
|
|
+ for (FsCourseQuestionBank questionBank : questions) {
|
|
|
+ FsCourseQuestionBank correctAnswer = correctAnswersMap.get(questionBank.getId());
|
|
|
+ if (correctAnswer == null || correctAnswer.getType() == null) {
|
|
|
+ correctAnswer = new FsCourseQuestionBank();
|
|
|
+ correctAnswer.setId(questionBank.getId());
|
|
|
+ incorrectQuestions.add(correctAnswer);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (correctAnswer.getType() == 1) {
|
|
|
+ if (questionBank.getAnswer() != null && questionBank.getAnswer().equals(correctAnswer.getAnswer())) {
|
|
|
+ thisRightCount++;
|
|
|
+ } else {
|
|
|
+ correctAnswer.setAnswer(null);
|
|
|
+ incorrectQuestions.add(correctAnswer);
|
|
|
+ }
|
|
|
+ } else if (correctAnswer.getType() == 2) {
|
|
|
+ String[] userAnswers = convertStringToArray(questionBank.getAnswer());
|
|
|
+ String[] correctAnswers = convertStringToArray(correctAnswer.getAnswer());
|
|
|
+
|
|
|
+ Arrays.sort(userAnswers);
|
|
|
+ Arrays.sort(correctAnswers);
|
|
|
+
|
|
|
+ if (Arrays.equals(userAnswers, correctAnswers)) {
|
|
|
+ thisRightCount++;
|
|
|
+ } else {
|
|
|
+ correctAnswer.setAnswer(null);
|
|
|
+ incorrectQuestions.add(correctAnswer);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+// }
|
|
|
+ }
|
|
|
+
|
|
|
+ FsCourseAnswerLogs logs = new FsCourseAnswerLogs();
|
|
|
+ logs.setWatchLogId(logId);
|
|
|
+ logs.setUserId(param.getUserId());
|
|
|
+ logs.setVideoId(param.getVideoId());
|
|
|
+ logs.setCourseId(param.getCourseId());
|
|
|
+ logs.setCompanyId(param.getCompanyId());
|
|
|
+ logs.setCompanyUserId(param.getCompanyUserId());
|
|
|
+ logs.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
|
|
|
+ logs.setQuestionJson(JSONObject.toJSONString(param.getQuestions()));
|
|
|
+ logs.setCreateTime(new Date());
|
|
|
+ logs.setPeriodId(param.getPeriodId());
|
|
|
+
|
|
|
+ int questionCount = questions == null ? 0 : questions.size();
|
|
|
+ if (thisRightCount == questionCount) {
|
|
|
+ logs.setIsRight(1);
|
|
|
+ courseAnswerLogsMapper.insertFsCourseAnswerLogs(logs);
|
|
|
+ return R.ok("答题成功");
|
|
|
+ }
|
|
|
+ logs.setIsRight(0);
|
|
|
+ courseAnswerLogsMapper.insertFsCourseAnswerLogs(logs);
|
|
|
+ return R.ok("答题失败").put("incorrectQuestions", incorrectQuestions).put("remain", remainCount);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public R getCourseAnswerStatusForUserApp(FsCourseAnswerStatusQueryParam param) {
|
|
|
+ if (param == null || param.getUserId() == null || param.getVideoId() == null) {
|
|
|
+ return R.error("参数不完整");
|
|
|
+ }
|
|
|
+ String lockKey = REDIS_LOCK_APP_COURSE_ANSWER_STATUS + param.getUserId() + ":" + param.getVideoId()
|
|
|
+ + (param.getPeriodId() != null ? ":" + param.getPeriodId() : "");
|
|
|
+ boolean locked = redisCache.setIfAbsent(lockKey, "1", 30L, TimeUnit.SECONDS);
|
|
|
+ if (!locked) {
|
|
|
+ return R.error("操作过于频繁,请稍后重试");
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ FsCourseAnswerStatusVO vo = buildCourseAnswerStatusForUserApp(param);
|
|
|
+ return R.ok().put("data", vo);
|
|
|
+ } finally {
|
|
|
+ redisCache.deleteObject(lockKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public R claimPublicCourseAnswerIntegralReward(FsCourseAnswerStatusQueryParam param) {
|
|
|
+ if (param == null || param.getUserId() == null || param.getVideoId() == null) {
|
|
|
+ return R.error("参数不完整");
|
|
|
+ }
|
|
|
+ String lockKey = REDIS_LOCK_APP_COURSE_ANSWER_CLAIM + param.getUserId() + ":" + param.getVideoId()
|
|
|
+ + (param.getPeriodId() != null ? ":" + param.getPeriodId() : "");
|
|
|
+ boolean locked = redisCache.setIfAbsent(lockKey, "1", 45L, TimeUnit.SECONDS);
|
|
|
+ if (!locked) {
|
|
|
+ return R.error("操作过于频繁,请稍后重试");
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ FsCourseAnswerLogs everRight = selectEverSucceededAnswerLogForApp(
|
|
|
+ param.getVideoId(), param.getUserId(), param.getQwUserId(), param.getPeriodId(), param.getLinkType());
|
|
|
+ if (everRight != null) {
|
|
|
+ return R.ok("你已完成过当前课程的答题");
|
|
|
+ }
|
|
|
+
|
|
|
+ FsUserCourseStudyLog studyRow = fsUserCourseStudyLogMapper.selectFsUserCourseStudyLogByVideoToday(param.getUserId(), param.getVideoId());
|
|
|
+ if (studyRow == null) {
|
|
|
+ return R.error("检测学习数据丢失,请联系管理处理");
|
|
|
+ }
|
|
|
+
|
|
|
+ FsCourseAnswerLogs rightLog;
|
|
|
+ if (param.getLinkType() != null && param.getLinkType() == 1) {
|
|
|
+ rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideo(param.getVideoId(), param.getUserId(), null);
|
|
|
+ } else {
|
|
|
+ FsCourseWatchLog watchLog = resolveCourseWatchLogForApp(
|
|
|
+ param.getVideoId(), param.getUserId(), param.getCompanyUserId(), param.getPeriodId());
|
|
|
+ if (watchLog == null) {
|
|
|
+ return R.error("无看课记录");
|
|
|
+ }
|
|
|
+ if (watchLog.getLogType() == null || watchLog.getLogType() != 2) {
|
|
|
+ return R.error("未完课");
|
|
|
+ }
|
|
|
+ if (param.getPeriodId() != null) {
|
|
|
+ rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideoWithPeriodId(
|
|
|
+ param.getVideoId(), param.getUserId(), param.getQwUserId(), param.getPeriodId());
|
|
|
+ } else {
|
|
|
+ rightLog = courseAnswerLogsMapper.selectRightLogByCourseVideo(param.getVideoId(), param.getUserId(), param.getQwUserId());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rightLog == null) {
|
|
|
+ return R.error("尚未答题正确,无法领取积分");
|
|
|
+ }
|
|
|
+
|
|
|
+ FsUserCourseVideo video = courseVideoMapper.selectFsUserCourseVideoByVideoId(param.getVideoId());
|
|
|
+ if (video == null) {
|
|
|
+ return R.error("视频不存在");
|
|
|
+ }
|
|
|
+ Integer rewardPoints = video.getIntegralReward();
|
|
|
+ if (rewardPoints == null || rewardPoints <= 0) {
|
|
|
+ return R.error("本节未配置答题积分或未开启");
|
|
|
+ }
|
|
|
+
|
|
|
+ String businessId = resolveAnswerIntegralBusinessId(param.getVideoId(), param.getPeriodId());
|
|
|
+ FsUserIntegralLogs existed = fsUserIntegralLogsMapper.selectAnswerRewardIntegralLog(param.getUserId(), businessId);
|
|
|
+ if (existed != null) {
|
|
|
+ return R.error("答题积分已领取");
|
|
|
+ }
|
|
|
+
|
|
|
+ FsUser user = fsUserMapper.selectFsUserByUserId(param.getUserId());
|
|
|
+ if (user == null) {
|
|
|
+ return R.error("用户不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ long baseIntegral = user.getIntegral() != null ? user.getIntegral() : 0L;
|
|
|
+ long reward = rewardPoints.longValue();
|
|
|
+ long newBalance = baseIntegral + reward;
|
|
|
+
|
|
|
+ FsUser upd = new FsUser();
|
|
|
+ upd.setUserId(param.getUserId());
|
|
|
+ upd.setIntegral(newBalance);
|
|
|
+ fsUserMapper.updateFsUser(upd);
|
|
|
+
|
|
|
+ FsUserIntegralLogs integralLogs = new FsUserIntegralLogs();
|
|
|
+ integralLogs.setIntegral(reward);
|
|
|
+ integralLogs.setUserId(param.getUserId());
|
|
|
+ integralLogs.setBalance(newBalance);
|
|
|
+ integralLogs.setLogType(FsUserIntegralLogTypeEnum.TYPE_17.getValue());
|
|
|
+ integralLogs.setBusinessId(businessId);
|
|
|
+ integralLogs.setRemark(FsUserIntegralLogTypeEnum.TYPE_17.getDesc());
|
|
|
+ integralLogs.setCreateTime(new Date());
|
|
|
+ fsUserIntegralLogsService.insertFsUserIntegralLogs(integralLogs);
|
|
|
+
|
|
|
+ return R.ok("领取成功").put("integral", rewardPoints).put("balance", newBalance);
|
|
|
+ } finally {
|
|
|
+ redisCache.deleteObject(lockKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static String resolveAnswerIntegralBusinessId(Long videoId, Long periodId) {
|
|
|
+ if (periodId != null) {
|
|
|
+ return videoId + ":p:" + periodId;
|
|
|
+ }
|
|
|
+ return String.valueOf(videoId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 与 {@link #courseAnswerForUserAppLocked} 前置条件、错题统计、是否已有正确记录判定保持一致。
|
|
|
+ */
|
|
|
+ private FsCourseAnswerStatusVO buildCourseAnswerStatusForUserApp(FsCourseAnswerStatusQueryParam param) {
|
|
|
+ FsCourseAnswerStatusVO vo = new FsCourseAnswerStatusVO();
|
|
|
+ vo.setMaxAttempts(APP_USER_ANSWER_MAX_ATTEMPTS);
|
|
|
+
|
|
|
+ FsCourseAnswerLogs everRight = selectEverSucceededAnswerLogForApp(
|
|
|
+ param.getVideoId(), param.getUserId(), param.getQwUserId(), param.getPeriodId(), param.getLinkType());
|
|
|
+ if (everRight != null) {
|
|
|
+ fillCompleted(vo);
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ FsUserCourseStudyLog studyRow = fsUserCourseStudyLogMapper.selectFsUserCourseStudyLogByVideoToday(param.getUserId(), param.getVideoId());
|
|
|
+ if (studyRow == null) {
|
|
|
+ vo.setStatus(-1);
|
|
|
+ vo.setStatusText("检测学习数据丢失,请联系管理处理");
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (param.getLinkType() != null && param.getLinkType() == 1) {
|
|
|
+ int errorCount = courseAnswerLogsMapper.selectErrorCountByCourseVideoToday(param.getVideoId(), param.getUserId(), null, null);
|
|
|
+ vo.setErrorCount(errorCount);
|
|
|
+ fillProgressByErrorCount(vo, errorCount);
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ FsCourseWatchLog watchLog = resolveCourseWatchLogForApp(
|
|
|
+ param.getVideoId(), param.getUserId(), param.getCompanyUserId(), param.getPeriodId());
|
|
|
+ if (watchLog == null) {
|
|
|
+ vo.setStatus(-2);
|
|
|
+ vo.setStatusText("无看课记录");
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+ if (watchLog.getLogType() == null || watchLog.getLogType() != 2) {
|
|
|
+ vo.setStatus(-3);
|
|
|
+ vo.setStatusText("未完课");
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ int errorCount = courseAnswerLogsMapper.selectErrorCountByCourseVideoToday(
|
|
|
+ param.getVideoId(), param.getUserId(), param.getQwUserId(), watchLog.getProject());
|
|
|
+ vo.setErrorCount(errorCount);
|
|
|
+ fillProgressByErrorCount(vo, errorCount);
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void fillCompleted(FsCourseAnswerStatusVO vo) {
|
|
|
+ vo.setStatus(4);
|
|
|
+ vo.setStatusText("已完成答题");
|
|
|
+ vo.setErrorCount(null);
|
|
|
+ vo.setRemainWrongAttempts(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void fillProgressByErrorCount(FsCourseAnswerStatusVO vo, int errorCount) {
|
|
|
+ if (errorCount >= APP_USER_ANSWER_MAX_ATTEMPTS) {
|
|
|
+ vo.setStatus(3);
|
|
|
+ vo.setStatusText("答题达到上限");
|
|
|
+ vo.setRemainWrongAttempts(0);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (errorCount == 0) {
|
|
|
+ vo.setStatus(1);
|
|
|
+ vo.setStatusText("未答题");
|
|
|
+ vo.setRemainWrongAttempts(APP_USER_ANSWER_MAX_ATTEMPTS);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ vo.setStatus(2);
|
|
|
+ vo.setStatusText("可以答题");
|
|
|
+ vo.setRemainWrongAttempts(APP_USER_ANSWER_MAX_ATTEMPTS - errorCount);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 与 {@link #courseAnswer} 中单选/多选判分规则一致。
|
|
|
*/
|