feat(review): 新增复习内容滚动展示与交互 API

- 新增 /review/feed 端点,合并报告和残片数据按时间倒序返回
- 新增 /review/report/{id} 和 /review/fragment/{id} 端点
- 支持复习内容在首页滚动展示及点击查看详情
This commit is contained in:
2026-05-23 19:22:55 +08:00
parent a7cb4a1167
commit 375ec8858c
6 changed files with 192 additions and 0 deletions
@@ -0,0 +1,50 @@
package com.guo.learningprogresstracker.controller;
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
import com.guo.learningprogresstracker.entity.CommonResult;
import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity;
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
import com.guo.learningprogresstracker.exception.NotFindEntitiesException;
import com.guo.learningprogresstracker.service.impl.ReviewServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 复习模块控制层 — 复习内容滚动展示与交互
*/
@RequestMapping("/review")
@RestController
@Validated
@RequiredArgsConstructor
public class ReviewController {
private final ReviewServiceImpl reviewService;
/**
* 获取复习 feed,合并报告和残片按时间倒序
*/
@GetMapping("/feed")
public CommonResult<List<ReviewFeedItem>> getReviewFeed(
@RequestParam(defaultValue = "30") int limit) {
return CommonResult.success(reviewService.getReviewFeed(limit));
}
/**
* 获取报告详情
*/
@GetMapping("/report/{id}")
public CommonResult<StudyReportsEntity> getReportDetail(@PathVariable int id) throws NotFindEntitiesException {
return CommonResult.success(reviewService.getReportDetail(id));
}
/**
* 获取残片详情
*/
@GetMapping("/fragment/{id}")
public CommonResult<StudyReportFragmentsEntity> getFragmentDetail(@PathVariable int id) throws NotFindEntitiesException {
return CommonResult.success(reviewService.getFragmentDetail(id));
}
}
@@ -0,0 +1,19 @@
package com.guo.learningprogresstracker.dto;
import lombok.Data;
import java.time.LocalDateTime;
/**
* 复习滚动 feed 条目,合并展示学习报告和残片
*/
@Data
public class ReviewFeedItem {
private Integer id;
private String sessionNum;
private String taskNum;
private String taskName;
private String sourceType; // REPORT | FRAGMENT
private String content;
private LocalDateTime createdTime;
}
@@ -0,0 +1,19 @@
package com.guo.learningprogresstracker.mapper;
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 复习模块自定义查询 Mapper
*/
@Mapper
public interface ReviewMapper {
/**
* 获取最近的学习报告和残片,合并排序后返回
*/
List<ReviewFeedItem> selectReviewFeed(@Param("limit") int limit);
}
@@ -0,0 +1,29 @@
package com.guo.learningprogresstracker.service;
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity;
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
import com.guo.learningprogresstracker.exception.NotFindEntitiesException;
import java.util.List;
/**
* 复习模块 Service 接口
*/
public interface ReviewService {
/**
* 获取复习 feed 列表,合并报告和残片按时间倒序
*/
List<ReviewFeedItem> getReviewFeed(int limit);
/**
* 获取报告详情
*/
StudyReportsEntity getReportDetail(int id) throws NotFindEntitiesException;
/**
* 获取残片详情
*/
StudyReportFragmentsEntity getFragmentDetail(int id) throws NotFindEntitiesException;
}
@@ -0,0 +1,44 @@
package com.guo.learningprogresstracker.service.impl;
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity;
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
import com.guo.learningprogresstracker.exception.NotFindEntitiesException;
import com.guo.learningprogresstracker.mapper.ReviewMapper;
import com.guo.learningprogresstracker.mapper.StudyReportFragmentsMapper;
import com.guo.learningprogresstracker.mapper.StudyReportsMapper;
import com.guo.learningprogresstracker.service.ReviewService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
* 复习模块 Service 实现
*/
@Service
@RequiredArgsConstructor
public class ReviewServiceImpl implements ReviewService {
private final ReviewMapper reviewMapper;
private final StudyReportsMapper studyReportsMapper;
private final StudyReportFragmentsMapper studyReportFragmentsMapper;
@Override
public List<ReviewFeedItem> getReviewFeed(int limit) {
return reviewMapper.selectReviewFeed(limit);
}
@Override
public StudyReportsEntity getReportDetail(int id) throws NotFindEntitiesException {
return Optional.ofNullable(studyReportsMapper.selectById(id))
.orElseThrow(() -> new NotFindEntitiesException("学习报告[" + id + "]不存在"));
}
@Override
public StudyReportFragmentsEntity getFragmentDetail(int id) throws NotFindEntitiesException {
return Optional.ofNullable(studyReportFragmentsMapper.selectById(id))
.orElseThrow(() -> new NotFindEntitiesException("学习残片[" + id + "]不存在"));
}
}
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis-org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.guo.learningprogresstracker.mapper.ReviewMapper">
<select id="selectReviewFeed" resultType="com.guo.learningprogresstracker.dto.ReviewFeedItem">
SELECT
combined.id,
combined.session_num AS sessionNum,
combined.source_type AS sourceType,
combined.content,
combined.created_time AS createdTime,
t.task_num AS taskNum,
t.task_name AS taskName
FROM (
SELECT r.id, r.session_num, r.content, r.created_time, 'REPORT' AS source_type
FROM study_reports r
WHERE r.deleted = 0
UNION ALL
SELECT f.id, f.session_num, f.content, f.created_time, 'FRAGMENT' AS source_type
FROM study_report_fragments f
WHERE f.deleted = 0
) combined
JOIN study_sessions ss ON combined.session_num = ss.session_num AND ss.deleted = 0
JOIN tasks t ON ss.task_num = t.task_num AND t.deleted = 0
ORDER BY combined.created_time DESC
LIMIT #{limit}
</select>
</mapper>