|
@@ -15,6 +15,7 @@ import com.xuekairui.quiz.mapper.QuizSubjectMapper;
|
|
|
import com.xuekairui.quiz.mapper.QuizTrackLogMapper;
|
|
import com.xuekairui.quiz.mapper.QuizTrackLogMapper;
|
|
|
import com.xuekairui.quiz.mapper.QuizUserRecordMapper;
|
|
import com.xuekairui.quiz.mapper.QuizUserRecordMapper;
|
|
|
import com.xuekairui.quiz.service.QuizTrackService;
|
|
import com.xuekairui.quiz.service.QuizTrackService;
|
|
|
|
|
+import com.xuekairui.quiz.util.QuizDedupUtils;
|
|
|
import com.xuekairui.user.entity.User;
|
|
import com.xuekairui.user.entity.User;
|
|
|
import com.xuekairui.user.mapper.UserMapper;
|
|
import com.xuekairui.user.mapper.UserMapper;
|
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.RequiredArgsConstructor;
|
|
@@ -147,7 +148,19 @@ public class QuizTrackServiceImpl extends ServiceImpl<QuizTrackLogMapper, QuizTr
|
|
|
userItem.put("phone", phone);
|
|
userItem.put("phone", phone);
|
|
|
userItem.put("registerTime", user != null ? user.getCreateTime() : "");
|
|
userItem.put("registerTime", user != null ? user.getCreateTime() : "");
|
|
|
userItem.put("firstUseTime", records.stream().map(QuizUserRecord::getCreateTime).min(LocalDateTime::compareTo).orElse(null));
|
|
userItem.put("firstUseTime", records.stream().map(QuizUserRecord::getCreateTime).min(LocalDateTime::compareTo).orElse(null));
|
|
|
- userItem.put("totalAnswerCount", records.stream().mapToInt(r -> r.getTotalCount() != null ? r.getTotalCount() : 0).sum());
|
|
|
|
|
|
|
+ // 按题目去重统计:同一道题只算一次,与排行榜口径一致
|
|
|
|
|
+ List<QuizUserRecord> sortedForDedup = records.stream()
|
|
|
|
|
+ .sorted((a, b) -> {
|
|
|
|
|
+ LocalDateTime ta = a.getCreateTime();
|
|
|
|
|
+ LocalDateTime tb = b.getCreateTime();
|
|
|
|
|
+ if (ta == null && tb == null) return 0;
|
|
|
|
|
+ if (ta == null) return 1;
|
|
|
|
|
+ if (tb == null) return -1;
|
|
|
|
|
+ return tb.compareTo(ta);
|
|
|
|
|
+ })
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+ Map<Long, Integer> latestMap = QuizDedupUtils.extractLatestAnswerMap(sortedForDedup);
|
|
|
|
|
+ userItem.put("totalAnswerCount", latestMap.size());
|
|
|
userItem.put("lastAnswerTime", records.stream().map(QuizUserRecord::getCreateTime).max(LocalDateTime::compareTo).orElse(null));
|
|
userItem.put("lastAnswerTime", records.stream().map(QuizUserRecord::getCreateTime).max(LocalDateTime::compareTo).orElse(null));
|
|
|
userList.add(userItem);
|
|
userList.add(userItem);
|
|
|
}
|
|
}
|
|
@@ -189,6 +202,24 @@ public class QuizTrackServiceImpl extends ServiceImpl<QuizTrackLogMapper, QuizTr
|
|
|
|
|
|
|
|
// 简单分页
|
|
// 简单分页
|
|
|
long total = userRecordMapper.selectCount(wrapper);
|
|
long total = userRecordMapper.selectCount(wrapper);
|
|
|
|
|
+
|
|
|
|
|
+ // 去重汇总统计(同一道题只算一次,与排行榜口径一致)
|
|
|
|
|
+ List<QuizUserRecord> allRecordsForSummary = userRecordMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<QuizUserRecord>()
|
|
|
|
|
+ .eq(subjectId != null, QuizUserRecord::getSubjectId, subjectId)
|
|
|
|
|
+ .eq(answerType != null, QuizUserRecord::getAnswerType, answerType)
|
|
|
|
|
+ .eq(completeStatus != null, QuizUserRecord::getStatus, completeStatus)
|
|
|
|
|
+ .ge(QuizUserRecord::getCreateTime, timeRange[0])
|
|
|
|
|
+ .le(QuizUserRecord::getCreateTime, timeRange[1])
|
|
|
|
|
+ .orderByDesc(QuizUserRecord::getCreateTime)
|
|
|
|
|
+ );
|
|
|
|
|
+ Map<Long, Integer> summaryLatestMap = QuizDedupUtils.extractLatestAnswerMap(allRecordsForSummary);
|
|
|
|
|
+ int summaryTotalCount = summaryLatestMap.size();
|
|
|
|
|
+ int summaryCorrectCount = 0;
|
|
|
|
|
+ for (Integer isCorrect : summaryLatestMap.values()) {
|
|
|
|
|
+ if (isCorrect != null && isCorrect == 1) summaryCorrectCount++;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
wrapper.last("LIMIT " + (pageNo - 1) * pageSize + "," + pageSize);
|
|
wrapper.last("LIMIT " + (pageNo - 1) * pageSize + "," + pageSize);
|
|
|
List<QuizUserRecord> records = userRecordMapper.selectList(wrapper);
|
|
List<QuizUserRecord> records = userRecordMapper.selectList(wrapper);
|
|
|
|
|
|
|
@@ -239,6 +270,14 @@ public class QuizTrackServiceImpl extends ServiceImpl<QuizTrackLogMapper, QuizTr
|
|
|
Map<String, Object> result = new LinkedHashMap<>();
|
|
Map<String, Object> result = new LinkedHashMap<>();
|
|
|
result.put("total", total);
|
|
result.put("total", total);
|
|
|
result.put("list", list);
|
|
result.put("list", list);
|
|
|
|
|
+ // 去重汇总统计(与排行榜口径一致)
|
|
|
|
|
+ Map<String, Object> summary = new LinkedHashMap<>();
|
|
|
|
|
+ summary.put("totalCount", summaryTotalCount);
|
|
|
|
|
+ summary.put("correctCount", summaryCorrectCount);
|
|
|
|
|
+ summary.put("wrongCount", summaryTotalCount - summaryCorrectCount);
|
|
|
|
|
+ summary.put("correctRate", summaryTotalCount > 0
|
|
|
|
|
+ ? Math.round(summaryCorrectCount * 10000.0 / summaryTotalCount) / 100.0 : 0);
|
|
|
|
|
+ result.put("summary", summary);
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -297,7 +336,11 @@ public class QuizTrackServiceImpl extends ServiceImpl<QuizTrackLogMapper, QuizTr
|
|
|
|
|
|
|
|
Map<String, Object> item = new LinkedHashMap<>();
|
|
Map<String, Object> item = new LinkedHashMap<>();
|
|
|
item.put("questionId", qid);
|
|
item.put("questionId", qid);
|
|
|
- item.put("stem", question.getStem() != null && question.getStem().length() > 50 ? question.getStem().substring(0, 50) + "..." : question.getStem());
|
|
|
|
|
|
|
+ item.put("stem", question.getStem());
|
|
|
|
|
+ item.put("options", question.getOptions());
|
|
|
|
|
+ item.put("answer", question.getAnswer());
|
|
|
|
|
+ item.put("analysis", question.getAnalysis());
|
|
|
|
|
+ item.put("questionType", question.getQuestionType());
|
|
|
item.put("subjectName", subject != null ? subject.getName() : "");
|
|
item.put("subjectName", subject != null ? subject.getName() : "");
|
|
|
item.put("chapterName", chapter != null ? chapter.getName() : "");
|
|
item.put("chapterName", chapter != null ? chapter.getName() : "");
|
|
|
item.put("wrongTimes", wrongCount);
|
|
item.put("wrongTimes", wrongCount);
|
|
@@ -507,12 +550,12 @@ public class QuizTrackServiceImpl extends ServiceImpl<QuizTrackLogMapper, QuizTr
|
|
|
for (Map<String, Object> item : list) {
|
|
for (Map<String, Object> item : list) {
|
|
|
List<Object> row = new ArrayList<>();
|
|
List<Object> row = new ArrayList<>();
|
|
|
row.add(item.get("userId"));
|
|
row.add(item.get("userId"));
|
|
|
- row.add(item.get("answerType"));
|
|
|
|
|
- row.add(item.get("subjectId"));
|
|
|
|
|
|
|
+ row.add(item.get("answerTypeName"));
|
|
|
|
|
+ row.add(item.get("subjectName"));
|
|
|
row.add(item.get("totalCount"));
|
|
row.add(item.get("totalCount"));
|
|
|
row.add(item.get("correctCount"));
|
|
row.add(item.get("correctCount"));
|
|
|
row.add(item.get("correctRate"));
|
|
row.add(item.get("correctRate"));
|
|
|
- row.add(item.get("status"));
|
|
|
|
|
|
|
+ row.add(item.get("statusName"));
|
|
|
row.add(item.get("createTime"));
|
|
row.add(item.get("createTime"));
|
|
|
data.add(row);
|
|
data.add(row);
|
|
|
}
|
|
}
|
|
@@ -523,6 +566,8 @@ public class QuizTrackServiceImpl extends ServiceImpl<QuizTrackLogMapper, QuizTr
|
|
|
List<List<String>> head = new ArrayList<>();
|
|
List<List<String>> head = new ArrayList<>();
|
|
|
head.add(Collections.singletonList("排名"));
|
|
head.add(Collections.singletonList("排名"));
|
|
|
head.add(Collections.singletonList("题干"));
|
|
head.add(Collections.singletonList("题干"));
|
|
|
|
|
+ head.add(Collections.singletonList("选项"));
|
|
|
|
|
+ head.add(Collections.singletonList("正确答案"));
|
|
|
head.add(Collections.singletonList("科目"));
|
|
head.add(Collections.singletonList("科目"));
|
|
|
head.add(Collections.singletonList("考点"));
|
|
head.add(Collections.singletonList("考点"));
|
|
|
head.add(Collections.singletonList("错误次数"));
|
|
head.add(Collections.singletonList("错误次数"));
|
|
@@ -531,12 +576,31 @@ public class QuizTrackServiceImpl extends ServiceImpl<QuizTrackLogMapper, QuizTr
|
|
|
return head;
|
|
return head;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private String formatOptions(Object options) {
|
|
|
|
|
+ if (options == null) return "";
|
|
|
|
|
+ if (options instanceof List<?> list) {
|
|
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
|
|
+ for (Object o : list) {
|
|
|
|
|
+ if (o instanceof Map<?, ?> m) {
|
|
|
|
|
+ String key = m.get("key") != null ? m.get("key").toString() : "";
|
|
|
|
|
+ String content = m.get("content") != null ? m.get("content").toString() : "";
|
|
|
|
|
+ if (sb.length() > 0) sb.append("\n");
|
|
|
|
|
+ sb.append(key).append(". ").append(content);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return sb.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+ return options.toString();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private List<List<Object>> buildWrongData(List<Map<String, Object>> list) {
|
|
private List<List<Object>> buildWrongData(List<Map<String, Object>> list) {
|
|
|
List<List<Object>> data = new ArrayList<>();
|
|
List<List<Object>> data = new ArrayList<>();
|
|
|
for (Map<String, Object> item : list) {
|
|
for (Map<String, Object> item : list) {
|
|
|
List<Object> row = new ArrayList<>();
|
|
List<Object> row = new ArrayList<>();
|
|
|
row.add(item.get("rank"));
|
|
row.add(item.get("rank"));
|
|
|
row.add(item.get("stem"));
|
|
row.add(item.get("stem"));
|
|
|
|
|
+ row.add(formatOptions(item.get("options")));
|
|
|
|
|
+ row.add(item.get("answer"));
|
|
|
row.add(item.get("subjectName"));
|
|
row.add(item.get("subjectName"));
|
|
|
row.add(item.get("chapterName"));
|
|
row.add(item.get("chapterName"));
|
|
|
row.add(item.get("wrongTimes"));
|
|
row.add(item.get("wrongTimes"));
|