feat: complete review workflow support

This commit is contained in:
2026-06-20 09:16:20 +08:00
parent 81543969d8
commit f170e85297
20 changed files with 718 additions and 50 deletions
@@ -1,7 +1,9 @@
package com.guo.learningprogresstracker;
import cn.dev33.satoken.stp.StpUtil;
import com.guo.learningprogresstracker.entity.TestTableEntity;
import com.guo.learningprogresstracker.service.impl.TestTableServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@@ -19,6 +21,11 @@ public class DataSourceTest {
@Autowired
TestTableServiceImpl testTableService;
@BeforeEach
void setUp() {
StpUtil.login("1", "test_driver");
}
@Test
public void testAdd(){
TestTableEntity testTableEntity = new TestTableEntity();
@@ -30,8 +37,12 @@ public class DataSourceTest {
@Test
public void testDelete(){
TestTableEntity testTableEntity = new TestTableEntity();
testTableEntity.setIdName("guo_test_delete");
testTableService.save(testTableEntity);
HashMap<String, Object> stringStringHashMap = new HashMap<>();
stringStringHashMap.put("id_name", "guo_test");
stringStringHashMap.put("id_name", "guo_test_delete");
boolean delete = testTableService.removeByMap(stringStringHashMap);
assertTrue(delete,"删除idName为guo_test的数据失败");
@@ -18,8 +18,6 @@ import org.springframework.test.web.servlet.MockMvc;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
@@ -45,12 +43,12 @@ class TaskControllerTest {
@BeforeEach
void setUp() {
StpUtil.login("测试用户-创建任务", "test_driver");
StpUtil.login("1", "test_driver");
}
@Test
void addTask() throws Exception {
TaskRequest taskRequest = getTaskRequest();
TaskRequest taskRequest = getTaskRequest("测试任务名称-" + System.nanoTime());
log.info("testInfo");
String s = jacksonObjectMapper.writeValueAsString(taskRequest);
System.out.println("Token Value: " + StpUtil.getTokenValue());
@@ -63,30 +61,16 @@ class TaskControllerTest {
}
private static TaskRequest getTaskRequest() {
TaskRequest taskRequest = mock(TaskRequest.class);
when(taskRequest.getId()).thenReturn(null);
// 设置学习任务的名称
when(taskRequest.getTaskName()).thenReturn("测试任务名称");
// 设置学习材料的存储URL
when(taskRequest.getMaterialUrl()).thenReturn("http://example.com/material");
// 设置用户设置的任务紧急性
when(taskRequest.getUrgency()).thenReturn(5);
// 设置用户设置的任务重要性
when(taskRequest.getImportance()).thenReturn(3);
// 设置任务的内容难度
when(taskRequest.getContentDifficulty()).thenReturn(4);
// 设置任务的未来价值
when(taskRequest.getFutureValue()).thenReturn(4);
// 设置用户对任务的主观优先级
when(taskRequest.getSubjectivePriority()).thenReturn(1);
private static TaskRequest getTaskRequest(String taskName) {
TaskRequest taskRequest = new TaskRequest();
taskRequest.setTaskName(taskName);
taskRequest.setTaskDescription("测试任务描述");
taskRequest.setMaterialUrl("http://example.com/material");
taskRequest.setUrgency(5);
taskRequest.setImportance(3);
taskRequest.setContentDifficulty(4);
taskRequest.setFutureValue(4);
taskRequest.setSubjectivePriority(1);
return taskRequest;
}
@@ -108,10 +92,8 @@ class TaskControllerTest {
@Test
void updateTask() throws Exception {
TaskRequest taskRequest = mock(TaskRequest.class);
when(taskRequest.getTaskName()).thenReturn("测试数据名称");
when(taskRequest.getMaterialUrl()).thenReturn("http://example.com/material");
when(taskRequest.getId()).thenReturn(1);
TaskRequest taskRequest = getTaskRequest("测试数据名称");
taskRequest.setId(1);
String s = jacksonObjectMapper.writeValueAsString(taskRequest);
mockMvc.perform(put("/tasks/"+taskRequest.getId())
@@ -126,9 +108,9 @@ class TaskControllerTest {
@Test
void deleteTask() throws Exception {
CommonResult commonResult = taskController.addTask(getTaskRequest());
CommonResult commonResult = taskController.addTask(getTaskRequest("待删除任务-" + System.nanoTime()));
assertEquals(commonResult.getCode(),200);
TaskEntity taskEntity = tasksServiceImpl.getOne(Wrappers.<TaskEntity>lambdaQuery().eq(TaskEntity::getTaskNum, commonResult.getMessage()));
TaskEntity taskEntity = tasksServiceImpl.getOne(Wrappers.<TaskEntity>lambdaQuery().eq(TaskEntity::getTaskNum, commonResult.getData()));
assertNotNull(taskEntity,"未能找到新创建的任务实体");
mockMvc.perform(delete("/tasks/"+taskEntity.getId())
.header("satoken",StpUtil.getTokenValue()))
@@ -145,4 +127,4 @@ class TaskControllerTest {
.andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.data").exists());
}
}
}
@@ -0,0 +1,175 @@
package com.guo.learningprogresstracker.service.impl;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.guo.learningprogresstracker.dto.ReviewFeedItem;
import com.guo.learningprogresstracker.dto.request.CreateReviewApplicationRequest;
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.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;
import com.guo.learningprogresstracker.mapper.TasksMapper;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.time.LocalDateTime;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ReviewServiceImplTest {
@BeforeAll
static void initTableInfo() {
MybatisConfiguration configuration = new MybatisConfiguration();
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), StudyReportsEntity.class);
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), StudyReportFragmentsEntity.class);
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), StudySessionsEntity.class);
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), TaskEntity.class);
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), ReviewApplicationEntity.class);
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(configuration, ""), ReviewMindMapEntity.class);
}
@InjectMocks
private ReviewServiceImpl reviewService;
@Mock
private StudyReportsMapper studyReportsMapper;
@Mock
private StudyReportFragmentsMapper studyReportFragmentsMapper;
@Mock
private StudySessionsMapper studySessionsMapper;
@Mock
private TasksMapper tasksMapper;
@Mock
private ReviewApplicationMapper reviewApplicationMapper;
@Mock
private ReviewMindMapMapper reviewMindMapMapper;
@Test
void getReviewFeed_shouldApplyFinalLimitAfterMerge() {
StudyReportsEntity report = new StudyReportsEntity();
report.setId(1);
report.setSessionNum("S1");
report.setContent("report");
report.setCreatedTime(LocalDateTime.now());
StudyReportFragmentsEntity fragment = new StudyReportFragmentsEntity();
fragment.setId(2);
fragment.setSessionNum("S1");
fragment.setContent("fragment");
fragment.setCreatedTime(LocalDateTime.now().minusMinutes(1));
StudySessionsEntity session = new StudySessionsEntity();
session.setSessionNum("S1");
session.setTaskNum("T1");
TaskEntity task = new TaskEntity();
task.setTaskNum("T1");
task.setTaskName("Task");
when(studyReportsMapper.selectList(any())).thenReturn(List.of(report));
when(studyReportFragmentsMapper.selectList(any())).thenReturn(List.of(fragment));
when(studySessionsMapper.selectList(any())).thenReturn(List.of(session));
when(tasksMapper.selectList(any())).thenReturn(List.of(task));
List<ReviewFeedItem> items = reviewService.getReviewFeed(1, "recent");
assertEquals(1, items.size());
assertEquals("REPORT", items.get(0).getSourceType());
assertEquals("Task", items.get(0).getTaskName());
}
@Test
void createApplication_shouldUseDefaultStatusWhenStatusInvalid() throws Exception {
when(tasksMapper.exists(any())).thenReturn(true);
when(reviewApplicationMapper.insert(any())).thenAnswer(invocation -> {
ReviewApplicationEntity entity = invocation.getArgument(0);
entity.setId(7);
return 1;
});
CreateReviewApplicationRequest request = new CreateReviewApplicationRequest();
request.setTaskNum("T1");
request.setTitle("Build a demo");
request.setStatus("invalid");
ReviewApplicationEntity entity = reviewService.createApplication(request);
assertEquals(7, entity.getId());
assertEquals(ReviewApplicationStatusEnum.TODO.getCode(), entity.getStatus());
verify(reviewApplicationMapper).insert(any(ReviewApplicationEntity.class));
}
@Test
void upsertMindMap_shouldCreateWhenMissing() throws Exception {
when(tasksMapper.exists(any())).thenReturn(true);
when(reviewMindMapMapper.selectOne(any())).thenReturn(null);
when(reviewMindMapMapper.insert(any())).thenAnswer(invocation -> {
ReviewMindMapEntity entity = invocation.getArgument(0);
entity.setId(3);
return 1;
});
UpsertReviewMindMapRequest request = new UpsertReviewMindMapRequest();
request.setTitle("Map");
request.setContent("root");
request.setContentFormat("mermaid");
ReviewMindMapEntity entity = reviewService.upsertMindMap("T1", request);
assertNotNull(entity.getId());
assertEquals("T1", entity.getTaskNum());
assertEquals("MERMAID", entity.getContentFormat());
verify(reviewMindMapMapper).insert(any(ReviewMindMapEntity.class));
}
@Test
void upsertMindMap_shouldUpdateExisting() throws Exception {
ReviewMindMapEntity existing = new ReviewMindMapEntity();
existing.setId(5);
existing.setTaskNum("T1");
existing.setTitle("Old");
existing.setContent("old");
existing.setContentFormat("TEXT");
when(tasksMapper.exists(any())).thenReturn(true);
when(reviewMindMapMapper.selectOne(any())).thenReturn(existing);
UpsertReviewMindMapRequest request = new UpsertReviewMindMapRequest();
request.setTitle("New");
request.setContent("new");
request.setContentFormat("json");
ReviewMindMapEntity entity = reviewService.upsertMindMap("T1", request);
assertEquals(5, entity.getId());
assertEquals("New", entity.getTitle());
assertEquals("JSON", entity.getContentFormat());
verify(reviewMindMapMapper).updateById(existing);
}
}
+26
View File
@@ -0,0 +1,26 @@
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:lpt_test;MODE=MySQL;DATABASE_TO_LOWER=TRUE;CASE_INSENSITIVE_IDENTIFIERS=TRUE;NON_KEYWORDS=USER;DB_CLOSE_DELAY=-1
username: sa
password:
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
jackson:
time-zone: UTC
date-format: yyyy-MM-dd'T'HH:mm:ss'Z'
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.nologging.NoLoggingImpl
sa-token:
token-name: satoken
timeout: 2592000
active-timeout: -1
is-concurrent: true
is-share: true
token-style: uuid
is-log: false
@@ -0,0 +1,37 @@
insert into user (id, user_name, user_password, created_time, deleted)
values ('1', 'admin', '$2a$10$skdiSwut4R9pAvt2ZK5ct.6QNualPyJGiaxngFObdLrpAxRnzehBS', now(), 0);
insert into tasks (
id,
task_num,
task_name,
task_description,
urgency,
importance,
content_difficulty,
future_value,
subjective_priority,
calculated_priority,
material_url,
last_learning_status,
created_by,
created_time,
deleted
)
values (
1,
'TASK_TEST',
'测试任务名称',
'测试任务描述',
3,
3,
3,
3,
3,
3,
'http://example.com/material',
'未开始',
'1',
now(),
0
);
@@ -0,0 +1 @@
mock-maker-subclass