feat: 新增复习任务统计接口
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package com.guo.learningprogresstracker.controller;
|
package com.guo.learningprogresstracker.controller;
|
||||||
|
|
||||||
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
|
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
|
||||||
|
import com.guo.learningprogresstracker.dto.ReviewTaskStats;
|
||||||
import com.guo.learningprogresstracker.entity.CommonResult;
|
import com.guo.learningprogresstracker.entity.CommonResult;
|
||||||
import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity;
|
import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity;
|
||||||
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
|
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
|
||||||
@@ -32,6 +33,22 @@ public class ReviewController {
|
|||||||
return CommonResult.success(reviewService.getReviewFeed(limit));
|
return CommonResult.success(reviewService.getReviewFeed(limit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取任务维度的复习统计汇总
|
||||||
|
*/
|
||||||
|
@GetMapping("/tasks")
|
||||||
|
public CommonResult<List<ReviewTaskStats>> getReviewTaskStats() {
|
||||||
|
return CommonResult.success(reviewService.getReviewTaskStats());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定任务的复习统计汇总
|
||||||
|
*/
|
||||||
|
@GetMapping("/tasks/{taskNum}")
|
||||||
|
public CommonResult<ReviewTaskStats> getReviewTaskStats(@PathVariable String taskNum) {
|
||||||
|
return CommonResult.success(reviewService.getReviewTaskStats(taskNum));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定任务下的所有报告和残片
|
* 获取指定任务下的所有报告和残片
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.guo.learningprogresstracker.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务维度的复习统计数据。
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ReviewTaskStats {
|
||||||
|
private String taskNum;
|
||||||
|
private String taskName;
|
||||||
|
private long reportCount;
|
||||||
|
private long fragmentCount;
|
||||||
|
private double effectiveTime;
|
||||||
|
private long sessionCount;
|
||||||
|
private double todayEffectiveTime;
|
||||||
|
private double weekEffectiveTime;
|
||||||
|
private double avgEffectiveTime;
|
||||||
|
private double avgEffectivenessRatio;
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.guo.learningprogresstracker.service;
|
package com.guo.learningprogresstracker.service;
|
||||||
|
|
||||||
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
|
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
|
||||||
|
import com.guo.learningprogresstracker.dto.ReviewTaskStats;
|
||||||
import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity;
|
import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity;
|
||||||
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
|
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
|
||||||
import com.guo.learningprogresstracker.exception.NotFindEntitiesException;
|
import com.guo.learningprogresstracker.exception.NotFindEntitiesException;
|
||||||
@@ -17,6 +18,16 @@ public interface ReviewService {
|
|||||||
*/
|
*/
|
||||||
List<ReviewFeedItem> getReviewFeed(int limit);
|
List<ReviewFeedItem> getReviewFeed(int limit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有任务的复习统计数据
|
||||||
|
*/
|
||||||
|
List<ReviewTaskStats> getReviewTaskStats();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定任务的复习统计数据
|
||||||
|
*/
|
||||||
|
ReviewTaskStats getReviewTaskStats(String taskNum);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定任务下的所有报告和残片
|
* 获取指定任务下的所有报告和残片
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.guo.learningprogresstracker.service.impl;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
|
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
|
||||||
|
import com.guo.learningprogresstracker.dto.ReviewTaskStats;
|
||||||
import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity;
|
import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity;
|
||||||
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
|
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
|
||||||
import com.guo.learningprogresstracker.entity.StudySessionsEntity;
|
import com.guo.learningprogresstracker.entity.StudySessionsEntity;
|
||||||
@@ -14,7 +15,11 @@ import com.guo.learningprogresstracker.mapper.TasksMapper;
|
|||||||
import com.guo.learningprogresstracker.service.ReviewService;
|
import com.guo.learningprogresstracker.service.ReviewService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.time.DayOfWeek;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@@ -46,6 +51,35 @@ public class ReviewServiceImpl implements ReviewService {
|
|||||||
return mergeAndConvert(reports, fragments);
|
return mergeAndConvert(reports, fragments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ReviewTaskStats> getReviewTaskStats() {
|
||||||
|
List<TaskEntity> tasks = tasksMapper.selectList(
|
||||||
|
Wrappers.<TaskEntity>lambdaQuery()
|
||||||
|
.orderByDesc(TaskEntity::getCalculatedPriority));
|
||||||
|
|
||||||
|
List<ReviewTaskStats> stats = tasks.stream()
|
||||||
|
.map(this::newTaskStats)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
fillReviewTaskStats(stats);
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReviewTaskStats getReviewTaskStats(String taskNum) {
|
||||||
|
TaskEntity task = tasksMapper.selectList(
|
||||||
|
Wrappers.<TaskEntity>lambdaQuery()
|
||||||
|
.eq(TaskEntity::getTaskNum, taskNum)
|
||||||
|
.last("LIMIT 1"))
|
||||||
|
.stream()
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
|
||||||
|
ReviewTaskStats stats = newTaskStats(taskNum, task == null ? null : task.getTaskName());
|
||||||
|
fillReviewTaskStats(Collections.singletonList(stats));
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ReviewFeedItem> getTaskReview(String taskNum) {
|
public List<ReviewFeedItem> getTaskReview(String taskNum) {
|
||||||
List<String> sessionNums = studySessionsMapper.selectList(
|
List<String> sessionNums = studySessionsMapper.selectList(
|
||||||
@@ -151,4 +185,101 @@ public class ReviewServiceImpl implements ReviewService {
|
|||||||
Comparator.nullsLast(Comparator.reverseOrder())))
|
Comparator.nullsLast(Comparator.reverseOrder())))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ReviewTaskStats newTaskStats(TaskEntity task) {
|
||||||
|
return newTaskStats(task.getTaskNum(), task.getTaskName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ReviewTaskStats newTaskStats(String taskNum, String taskName) {
|
||||||
|
ReviewTaskStats stats = new ReviewTaskStats();
|
||||||
|
stats.setTaskNum(taskNum);
|
||||||
|
stats.setTaskName(taskName);
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillReviewTaskStats(List<ReviewTaskStats> statsList) {
|
||||||
|
Map<String, ReviewTaskStats> statsByTaskNum = statsList.stream()
|
||||||
|
.filter(item -> StringUtils.hasText(item.getTaskNum()))
|
||||||
|
.collect(Collectors.toMap(
|
||||||
|
ReviewTaskStats::getTaskNum,
|
||||||
|
item -> item,
|
||||||
|
(a, b) -> a,
|
||||||
|
LinkedHashMap::new));
|
||||||
|
|
||||||
|
if (statsByTaskNum.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<StudySessionsEntity> sessions = studySessionsMapper.selectList(
|
||||||
|
Wrappers.<StudySessionsEntity>lambdaQuery()
|
||||||
|
.in(StudySessionsEntity::getTaskNum, statsByTaskNum.keySet()));
|
||||||
|
|
||||||
|
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
|
||||||
|
LocalDateTime weekStart = LocalDate.now().with(DayOfWeek.MONDAY).atStartOfDay();
|
||||||
|
Map<String, String> sessionToTaskNum = new HashMap<>();
|
||||||
|
Map<String, Double> effectivenessRatioSum = new HashMap<>();
|
||||||
|
|
||||||
|
for (StudySessionsEntity session : sessions) {
|
||||||
|
ReviewTaskStats stats = statsByTaskNum.get(session.getTaskNum());
|
||||||
|
if (stats == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.hasText(session.getSessionNum())) {
|
||||||
|
sessionToTaskNum.put(session.getSessionNum(), session.getTaskNum());
|
||||||
|
}
|
||||||
|
|
||||||
|
double effectiveTime = session.getEffectiveTime();
|
||||||
|
stats.setSessionCount(stats.getSessionCount() + 1);
|
||||||
|
stats.setEffectiveTime(stats.getEffectiveTime() + effectiveTime);
|
||||||
|
effectivenessRatioSum.merge(session.getTaskNum(), session.getEffectivenessRatio(), Double::sum);
|
||||||
|
|
||||||
|
LocalDateTime startTime = session.getStartTime();
|
||||||
|
if (startTime != null && !startTime.isBefore(todayStart)) {
|
||||||
|
stats.setTodayEffectiveTime(stats.getTodayEffectiveTime() + effectiveTime);
|
||||||
|
}
|
||||||
|
if (startTime != null && !startTime.isBefore(weekStart)) {
|
||||||
|
stats.setWeekEffectiveTime(stats.getWeekEffectiveTime() + effectiveTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statsByTaskNum.forEach((taskNum, stats) -> {
|
||||||
|
if (stats.getSessionCount() > 0) {
|
||||||
|
stats.setAvgEffectiveTime(stats.getEffectiveTime() / stats.getSessionCount());
|
||||||
|
stats.setAvgEffectivenessRatio(effectivenessRatioSum.getOrDefault(taskNum, 0D) / stats.getSessionCount());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fillReportAndFragmentCount(sessionToTaskNum, statsByTaskNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillReportAndFragmentCount(Map<String, String> sessionToTaskNum,
|
||||||
|
Map<String, ReviewTaskStats> statsByTaskNum) {
|
||||||
|
if (sessionToTaskNum.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> sessionNums = sessionToTaskNum.keySet();
|
||||||
|
List<StudyReportsEntity> reports = studyReportsMapper.selectList(
|
||||||
|
Wrappers.<StudyReportsEntity>lambdaQuery()
|
||||||
|
.in(StudyReportsEntity::getSessionNum, sessionNums)
|
||||||
|
.select(StudyReportsEntity::getSessionNum));
|
||||||
|
for (StudyReportsEntity report : reports) {
|
||||||
|
ReviewTaskStats stats = statsByTaskNum.get(sessionToTaskNum.get(report.getSessionNum()));
|
||||||
|
if (stats != null) {
|
||||||
|
stats.setReportCount(stats.getReportCount() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<StudyReportFragmentsEntity> fragments = studyReportFragmentsMapper.selectList(
|
||||||
|
Wrappers.<StudyReportFragmentsEntity>lambdaQuery()
|
||||||
|
.in(StudyReportFragmentsEntity::getSessionNum, sessionNums)
|
||||||
|
.select(StudyReportFragmentsEntity::getSessionNum));
|
||||||
|
for (StudyReportFragmentsEntity fragment : fragments) {
|
||||||
|
ReviewTaskStats stats = statsByTaskNum.get(sessionToTaskNum.get(fragment.getSessionNum()));
|
||||||
|
if (stats != null) {
|
||||||
|
stats.setFragmentCount(stats.getFragmentCount() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user