feat: complete review workflow support
This commit is contained in:
@@ -2,6 +2,11 @@ package com.guo.learningprogresstracker.service;
|
||||
|
||||
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
|
||||
import com.guo.learningprogresstracker.dto.ReviewTaskStats;
|
||||
import com.guo.learningprogresstracker.dto.request.CreateReviewApplicationRequest;
|
||||
import com.guo.learningprogresstracker.dto.request.UpdateReviewApplicationRequest;
|
||||
import com.guo.learningprogresstracker.dto.request.UpsertReviewMindMapRequest;
|
||||
import com.guo.learningprogresstracker.entity.ReviewApplicationEntity;
|
||||
import com.guo.learningprogresstracker.entity.ReviewMindMapEntity;
|
||||
import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity;
|
||||
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
|
||||
import com.guo.learningprogresstracker.exception.NotFindEntitiesException;
|
||||
@@ -16,7 +21,7 @@ public interface ReviewService {
|
||||
/**
|
||||
* 获取复习 feed 列表,合并报告和残片按时间倒序
|
||||
*/
|
||||
List<ReviewFeedItem> getReviewFeed(int limit);
|
||||
List<ReviewFeedItem> getReviewFeed(int limit, String mode);
|
||||
|
||||
/**
|
||||
* 获取所有任务的复习统计数据
|
||||
@@ -42,4 +47,34 @@ public interface ReviewService {
|
||||
* 获取残片详情
|
||||
*/
|
||||
StudyReportFragmentsEntity getFragmentDetail(int id) throws NotFindEntitiesException;
|
||||
|
||||
/**
|
||||
* 获取指定任务的应用场景列表
|
||||
*/
|
||||
List<ReviewApplicationEntity> getApplications(String taskNum) throws NotFindEntitiesException;
|
||||
|
||||
/**
|
||||
* 创建应用场景
|
||||
*/
|
||||
ReviewApplicationEntity createApplication(CreateReviewApplicationRequest request) throws NotFindEntitiesException;
|
||||
|
||||
/**
|
||||
* 更新应用场景
|
||||
*/
|
||||
ReviewApplicationEntity updateApplication(Integer id, UpdateReviewApplicationRequest request) throws NotFindEntitiesException;
|
||||
|
||||
/**
|
||||
* 删除应用场景
|
||||
*/
|
||||
void deleteApplication(Integer id) throws NotFindEntitiesException;
|
||||
|
||||
/**
|
||||
* 获取指定任务的思维导图
|
||||
*/
|
||||
ReviewMindMapEntity getMindMap(String taskNum) throws NotFindEntitiesException;
|
||||
|
||||
/**
|
||||
* 创建或更新指定任务的思维导图
|
||||
*/
|
||||
ReviewMindMapEntity upsertMindMap(String taskNum, UpsertReviewMindMapRequest request) throws NotFindEntitiesException;
|
||||
}
|
||||
|
||||
@@ -3,11 +3,19 @@ package com.guo.learningprogresstracker.service.impl;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
|
||||
import com.guo.learningprogresstracker.dto.ReviewTaskStats;
|
||||
import com.guo.learningprogresstracker.dto.request.CreateReviewApplicationRequest;
|
||||
import com.guo.learningprogresstracker.dto.request.UpdateReviewApplicationRequest;
|
||||
import com.guo.learningprogresstracker.dto.request.UpsertReviewMindMapRequest;
|
||||
import com.guo.learningprogresstracker.entity.ReviewApplicationEntity;
|
||||
import com.guo.learningprogresstracker.entity.ReviewMindMapEntity;
|
||||
import com.guo.learningprogresstracker.entity.StudyReportFragmentsEntity;
|
||||
import com.guo.learningprogresstracker.entity.StudyReportsEntity;
|
||||
import com.guo.learningprogresstracker.entity.StudySessionsEntity;
|
||||
import com.guo.learningprogresstracker.entity.TaskEntity;
|
||||
import com.guo.learningprogresstracker.enums.ReviewApplicationStatusEnum;
|
||||
import com.guo.learningprogresstracker.exception.NotFindEntitiesException;
|
||||
import com.guo.learningprogresstracker.mapper.ReviewApplicationMapper;
|
||||
import com.guo.learningprogresstracker.mapper.ReviewMindMapMapper;
|
||||
import com.guo.learningprogresstracker.mapper.StudyReportFragmentsMapper;
|
||||
import com.guo.learningprogresstracker.mapper.StudyReportsMapper;
|
||||
import com.guo.learningprogresstracker.mapper.StudySessionsMapper;
|
||||
@@ -31,24 +39,42 @@ import java.util.stream.Stream;
|
||||
@RequiredArgsConstructor
|
||||
public class ReviewServiceImpl implements ReviewService {
|
||||
|
||||
private static final int DEFAULT_LIMIT = 30;
|
||||
private static final int MAX_LIMIT = 100;
|
||||
private static final String RANDOM_MODE = "random";
|
||||
private static final String DEFAULT_MIND_MAP_FORMAT = "TEXT";
|
||||
|
||||
private final StudyReportsMapper studyReportsMapper;
|
||||
private final StudyReportFragmentsMapper studyReportFragmentsMapper;
|
||||
private final StudySessionsMapper studySessionsMapper;
|
||||
private final TasksMapper tasksMapper;
|
||||
private final ReviewApplicationMapper reviewApplicationMapper;
|
||||
private final ReviewMindMapMapper reviewMindMapMapper;
|
||||
|
||||
@Override
|
||||
public List<ReviewFeedItem> getReviewFeed(int limit) {
|
||||
List<StudyReportsEntity> reports = studyReportsMapper.selectList(
|
||||
Wrappers.<StudyReportsEntity>lambdaQuery()
|
||||
public List<ReviewFeedItem> getReviewFeed(int limit, String mode) {
|
||||
int safeLimit = normalizeLimit(limit);
|
||||
boolean random = RANDOM_MODE.equalsIgnoreCase(mode);
|
||||
|
||||
List<StudyReportsEntity> reports = random
|
||||
? studyReportsMapper.selectList(Wrappers.<StudyReportsEntity>lambdaQuery()
|
||||
.last("ORDER BY RAND() LIMIT " + safeLimit))
|
||||
: studyReportsMapper.selectList(Wrappers.<StudyReportsEntity>lambdaQuery()
|
||||
.orderByDesc(StudyReportsEntity::getCreatedTime)
|
||||
.last("LIMIT " + limit));
|
||||
.last("LIMIT " + safeLimit));
|
||||
|
||||
List<StudyReportFragmentsEntity> fragments = studyReportFragmentsMapper.selectList(
|
||||
Wrappers.<StudyReportFragmentsEntity>lambdaQuery()
|
||||
List<StudyReportFragmentsEntity> fragments = random
|
||||
? studyReportFragmentsMapper.selectList(Wrappers.<StudyReportFragmentsEntity>lambdaQuery()
|
||||
.last("ORDER BY RAND() LIMIT " + safeLimit))
|
||||
: studyReportFragmentsMapper.selectList(Wrappers.<StudyReportFragmentsEntity>lambdaQuery()
|
||||
.orderByDesc(StudyReportFragmentsEntity::getCreatedTime)
|
||||
.last("LIMIT " + limit));
|
||||
.last("LIMIT " + safeLimit));
|
||||
|
||||
return mergeAndConvert(reports, fragments);
|
||||
List<ReviewFeedItem> items = mergeAndConvert(reports, fragments);
|
||||
if (random) {
|
||||
Collections.shuffle(items);
|
||||
}
|
||||
return items.stream().limit(safeLimit).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -119,6 +145,77 @@ public class ReviewServiceImpl implements ReviewService {
|
||||
.orElseThrow(() -> new NotFindEntitiesException("学习残片[" + id + "]不存在"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ReviewApplicationEntity> getApplications(String taskNum) throws NotFindEntitiesException {
|
||||
ensureTaskExists(taskNum);
|
||||
return reviewApplicationMapper.selectList(
|
||||
Wrappers.<ReviewApplicationEntity>lambdaQuery()
|
||||
.eq(ReviewApplicationEntity::getTaskNum, taskNum)
|
||||
.orderByDesc(ReviewApplicationEntity::getLastModifiedTime)
|
||||
.orderByDesc(ReviewApplicationEntity::getCreatedTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReviewApplicationEntity createApplication(CreateReviewApplicationRequest request) throws NotFindEntitiesException {
|
||||
ensureTaskExists(request.getTaskNum());
|
||||
ReviewApplicationEntity entity = new ReviewApplicationEntity();
|
||||
entity.setTaskNum(request.getTaskNum());
|
||||
entity.setTitle(request.getTitle());
|
||||
entity.setDescription(request.getDescription());
|
||||
entity.setResourceUrl(request.getResourceUrl());
|
||||
entity.setStatus(normalizeApplicationStatus(request.getStatus()));
|
||||
reviewApplicationMapper.insert(entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReviewApplicationEntity updateApplication(Integer id, UpdateReviewApplicationRequest request) throws NotFindEntitiesException {
|
||||
ReviewApplicationEntity existing = Optional.ofNullable(reviewApplicationMapper.selectById(id))
|
||||
.orElseThrow(() -> new NotFindEntitiesException("应用场景[" + id + "]不存在"));
|
||||
existing.setTitle(request.getTitle());
|
||||
existing.setDescription(request.getDescription());
|
||||
existing.setResourceUrl(request.getResourceUrl());
|
||||
existing.setStatus(normalizeApplicationStatus(request.getStatus()));
|
||||
reviewApplicationMapper.updateById(existing);
|
||||
return existing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteApplication(Integer id) throws NotFindEntitiesException {
|
||||
ReviewApplicationEntity existing = Optional.ofNullable(reviewApplicationMapper.selectById(id))
|
||||
.orElseThrow(() -> new NotFindEntitiesException("应用场景[" + id + "]不存在"));
|
||||
reviewApplicationMapper.deleteById(existing.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReviewMindMapEntity getMindMap(String taskNum) throws NotFindEntitiesException {
|
||||
ensureTaskExists(taskNum);
|
||||
return reviewMindMapMapper.selectOne(
|
||||
Wrappers.<ReviewMindMapEntity>lambdaQuery()
|
||||
.eq(ReviewMindMapEntity::getTaskNum, taskNum)
|
||||
.last("LIMIT 1"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReviewMindMapEntity upsertMindMap(String taskNum, UpsertReviewMindMapRequest request) throws NotFindEntitiesException {
|
||||
ensureTaskExists(taskNum);
|
||||
ReviewMindMapEntity entity = getMindMap(taskNum);
|
||||
boolean create = entity == null;
|
||||
if (create) {
|
||||
entity = new ReviewMindMapEntity();
|
||||
entity.setTaskNum(taskNum);
|
||||
}
|
||||
entity.setTitle(request.getTitle());
|
||||
entity.setContent(request.getContent());
|
||||
entity.setContentFormat(normalizeMindMapFormat(request.getContentFormat()));
|
||||
if (create) {
|
||||
reviewMindMapMapper.insert(entity);
|
||||
} else {
|
||||
reviewMindMapMapper.updateById(entity);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将报告和残片合并转换为 ReviewFeedItem 列表,按创建时间倒序排列
|
||||
*/
|
||||
@@ -197,6 +294,35 @@ public class ReviewServiceImpl implements ReviewService {
|
||||
return stats;
|
||||
}
|
||||
|
||||
private int normalizeLimit(int limit) {
|
||||
if (limit <= 0) {
|
||||
return DEFAULT_LIMIT;
|
||||
}
|
||||
return Math.min(limit, MAX_LIMIT);
|
||||
}
|
||||
|
||||
private void ensureTaskExists(String taskNum) throws NotFindEntitiesException {
|
||||
if (!StringUtils.hasText(taskNum) || !tasksMapper.exists(
|
||||
Wrappers.<TaskEntity>lambdaQuery().eq(TaskEntity::getTaskNum, taskNum))) {
|
||||
throw new NotFindEntitiesException("任务[" + taskNum + "]不存在");
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeApplicationStatus(String status) {
|
||||
return ReviewApplicationStatusEnum.fromCodeOrDefault(status).getCode();
|
||||
}
|
||||
|
||||
private String normalizeMindMapFormat(String contentFormat) {
|
||||
if (!StringUtils.hasText(contentFormat)) {
|
||||
return DEFAULT_MIND_MAP_FORMAT;
|
||||
}
|
||||
String normalized = contentFormat.trim().toUpperCase(Locale.ROOT);
|
||||
if (!Set.of("TEXT", "MERMAID", "JSON").contains(normalized)) {
|
||||
return DEFAULT_MIND_MAP_FORMAT;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private void fillReviewTaskStats(List<ReviewTaskStats> statsList) {
|
||||
Map<String, ReviewTaskStats> statsByTaskNum = statsList.stream()
|
||||
.filter(item -> StringUtils.hasText(item.getTaskNum()))
|
||||
|
||||
Reference in New Issue
Block a user