完善框架结构,解决了validation和saToken的问题

This commit is contained in:
guo
2024-07-14 23:00:59 +08:00
parent b1358eaca0
commit e759ab2a75
14 changed files with 315 additions and 64 deletions
+5
View File
@@ -31,6 +31,11 @@
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
@@ -1,4 +1,4 @@
package com.guo.learningprogresstracker.utils;
package com.guo.learningprogresstracker.common;
import com.guo.learningprogresstracker.entity.CommonResult;
import com.guo.learningprogresstracker.exception.AppException;
@@ -8,8 +8,10 @@ import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
/**
* @author guo
@@ -44,5 +46,6 @@ public class GlobalExceptionHandler {
BindingResult bindingResult = ex.getBindingResult();
//code:400
return CommonResult.error(bindingResult.getFieldError().getDefaultMessage());
}
}
}
@@ -0,0 +1,15 @@
package com.guo.learningprogresstracker.common;
import jakarta.validation.groups.Default;
/**
* 全局数据验证分组
* GlobalValidationGroup -> OpenGroups ->ops
* @author guo
*/
public interface Ops {
// 创建组
interface CreateG extends Default {}
// 更新组
interface UpdateG {}
}
@@ -2,15 +2,11 @@ package com.guo.learningprogresstracker.config;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.guo.learningprogresstracker.entity.UserEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import java.time.LocalDateTime;
import java.util.Date;
/**
* 用于配置
@@ -34,6 +30,7 @@ public class AutoTableField implements MetaObjectHandler {
@Override
public void updateFill(MetaObject metaObject) {
log.info("update autoField");
this.strictInsertFill(metaObject, "lastModifiedBy", StpUtil::getLoginIdAsString, String.class);
this.strictInsertFill(metaObject, "lastModifiedTime", LocalDateTime::now, LocalDateTime.class);
}
@@ -0,0 +1,25 @@
package com.guo.learningprogresstracker.config;
import com.guo.learningprogresstracker.config.interceptor.Interceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
@Configuration
@Slf4j
public class SaTokenConfigure implements WebMvcConfigurer {
@Autowired
Interceptor interceptor;
// 注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册 Sa-Token 拦截器,校验规则为 StpUtil.checkLogin() 登录校验。
registry.addInterceptor(interceptor)
.addPathPatterns("/**")
.excludePathPatterns(Arrays.asList("/login","/tasks"));
}
}
@@ -0,0 +1,45 @@
package com.guo.learningprogresstracker.config.interceptor;
import cn.dev33.satoken.stp.StpUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @Author: ChangYu
* @Version 1.0
*/
/**
* 拦截器
*/
@Component
public class Interceptor implements HandlerInterceptor {
Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
/**
* token过期或失效时,由于前端基本都是AJAX请求,拦截器返回401,由前端负责跳转至OA登陆页面
* note:chang yu
*/
StpUtil.checkLogin();
//续签
StpUtil.updateLastActiveToNow();
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
@@ -1,11 +1,13 @@
package com.guo.learningprogresstracker.controller;
import cn.dev33.satoken.stp.StpUtil;
import com.guo.learningprogresstracker.common.Ops;
import com.guo.learningprogresstracker.entity.CommonResult;
import com.guo.learningprogresstracker.dto.request.TaskRequest;
import com.guo.learningprogresstracker.service.TasksService;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.config.Task;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -19,6 +21,7 @@ import org.springframework.web.bind.annotation.RestController;
* @author guo
*/
@RestController
@Validated
@RequiredArgsConstructor
public class TaskController {
@@ -26,21 +29,22 @@ public class TaskController {
@PostMapping("/tasks")
@Operation(summary = "添加新任务")
public CommonResult addTask(@RequestBody TaskRequest taskRequest) {
public CommonResult addTask(@RequestBody @Validated({Ops.CreateG.class}) TaskRequest taskRequest) {
System.out.println("Token: " + StpUtil.getTokenValue());
return CommonResult.success(tasksService.addTask(taskRequest));
}
@Operation(summary = "任务详情API-未完成")
@GetMapping("/tasks/{taskId}")
public CommonResult showTask(@PathVariable("taskId") String taskId) {
return CommonResult.success(tasksService.showTask(taskId));
public CommonResult getTask(@PathVariable("taskId") String taskId) {
return CommonResult.success(tasksService.getTask(taskId));
}
@PutMapping("/tasks/{taskId}")
@Operation(summary = "更新任务详情")
public CommonResult updateTask(@PathVariable("taskId") String taskId, @RequestBody Task updatedTask) {
// return CommonResult.success(tasksService.updateTask(taskId, updatedTask));
return null;
public CommonResult updateTask(@PathVariable("taskId") String taskId,@Validated(Ops.UpdateG.class) @RequestBody TaskRequest updatedTask) {
tasksService.updateTask(taskId, updatedTask);
return CommonResult.success();
}
@DeleteMapping("/tasks/{taskId}")
@@ -52,8 +56,7 @@ public class TaskController {
@GetMapping("/tasks")
@Operation(summary = "获取所有任务列表")
public CommonResult listTasks() {
// return CommonResult.success(tasksService.listTasks());
return null;
public CommonResult tasksList() {
return CommonResult.success(tasksService.taskList());
}
}
@@ -1,6 +1,11 @@
package com.guo.learningprogresstracker.dto.request;
import com.guo.learningprogresstracker.common.Ops;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Null;
import lombok.Data;
/**
@@ -8,11 +13,15 @@ import lombok.Data;
*/
@Data
public class TaskRequest {
static class Create{}
public interface Create{}
@Null(message = "创建时不可指定任务ID",groups = Ops.CreateG.class)
@NotEmpty(message = "更新时必须指定任务ID",groups = Ops.UpdateG.class)
private Integer id;
/**
* 学习任务的名称
*/
@NotEmpty(message = "必须指定非空的任务名称", groups = TaskRequest.Create.class)
@NotEmpty(message = "必须指定非空的任务名称", groups = Ops.CreateG.class)
private String taskName;
/**
@@ -23,31 +32,41 @@ public class TaskRequest {
/**
* 用户设置的任务紧急性
*/
@NotEmpty(message = "必须指定【任务紧急性】指标", groups = Create.class)
@NotNull(message = "必须指定【任务紧急性】指标", groups = Ops.CreateG.class)
@Min(value = 0,message = "urgency参数值必须大于等于0")
@Max(value = 5,message = "urgency参数值必须小于等于5")
private Integer urgency;
/**
* 用户设置的任务重要性
*/
@NotEmpty(message = "必须指定【任务重要性】指标", groups = Create.class)
@NotNull(message = "必须指定【任务重要性】指标", groups = Ops.CreateG.class)
@Min(value = 0,message = "importance参数值必须大于等于0")
@Max(value = 5,message = "importance参数值必须小于等于5")
private Integer importance;
/**
* 任务的内容难度
*/
@NotEmpty(message = "必须指定【内容难度】指标", groups = Create.class)
@NotNull(message = "必须指定【内容难度】指标", groups = Ops.CreateG.class)
@Min(value = 0,message = "contentDifficulty参数值必须大于等于0")
@Max(value = 5,message = "contentDifficulty参数值必须小于等于5")
private Integer contentDifficulty;
/**
* 任务的未来价值
*/
@NotEmpty(message = "必须指定【未来价值】指标", groups = Create.class)
@NotNull(message = "必须指定【未来价值】指标", groups = Ops.CreateG.class)
@Min(value = 0,message = "futureValue参数值必须大于等于0")
@Max(value = 5,message = "futureValue参数值必须小于等于5")
private Integer futureValue;
/**
* 用户对任务的主观优先级
*/
@NotEmpty(message = "必须指定【主观优先级】指标", groups = Create.class)
@NotNull(message = "必须指定【主观优先级】指标", groups = Ops.CreateG.class)
@Min(value = 0,message = "subjectivePriority参数值必须大于等于0")
@Max(value = 5,message = "subjectivePriority参数值必须小于等于5")
private Integer subjectivePriority;
}
@@ -1,23 +1,57 @@
package com.guo.learningprogresstracker.dto.response;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.util.List;
@Data
public class TaskInfoResponse {
// 任务列表
private List<TaskInfo> taskList;
private int id;
/**
* 任务编码
*/
private String taskNum;
/**
* 学习任务的名称
*/
private String taskName;
/**
* 学习材料的存储URL
*/
private String materialUrl;
/**
* 用户设置的任务紧急性
*/
private Integer urgency;
/**
* 用户设置的任务重要性
*/
private Integer importance;
/**
* 任务的内容难度
*/
private Integer contentDifficulty;
/**
* 任务的未来价值
*/
private Integer futureValue;
/**
* 用户对任务的主观优先级
*/
private Integer subjectivePriority;
/**
* 通过算法计算得出的任务优先级
*/
private Double calculatedPriority;
static class TaskInfo{
// 任务名称
private String taskName;
// 任务描述
private String taskDescription;
// 任务优先级
private Integer taskPriority;
// 上次该任务学习情况
private String lastLearningStatus;
}
}
@@ -0,0 +1,26 @@
package com.guo.learningprogresstracker.dto.response;
import lombok.Data;
import java.util.List;
/**
* 任务列表信息响应体
*/
@Data
public class TasksListInfoResponse {
// 任务列表
private List<TaskInfo> taskList;
static class TaskInfo{
// 任务名称
private String taskName;
// 任务描述
private String taskDescription;
// 任务优先级
private Integer taskPriority;
// 上次该任务学习情况
private String lastLearningStatus;
}
}
@@ -2,6 +2,7 @@ package com.guo.learningprogresstracker.mapStruct;
import com.guo.learningprogresstracker.dto.request.TaskRequest;
import com.guo.learningprogresstracker.dto.response.TaskInfoResponse;
import com.guo.learningprogresstracker.entity.TaskEntity;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
@@ -17,4 +18,5 @@ public interface TaskConvert {
TaskEntity taskRequestToTaskEntity(TaskRequest taskRequest);
TaskInfoResponse taskEntityToTaskInfoResponse(TaskEntity taskEntity);
}
@@ -2,6 +2,7 @@ package com.guo.learningprogresstracker.service;
import com.guo.learningprogresstracker.dto.request.TaskRequest;
import com.guo.learningprogresstracker.dto.response.TaskInfoResponse;
import com.guo.learningprogresstracker.dto.response.TasksListInfoResponse;
import com.guo.learningprogresstracker.entity.TaskEntity;
import com.baomidou.mybatisplus.extension.service.IService;
@@ -12,8 +13,11 @@ import com.baomidou.mybatisplus.extension.service.IService;
*/
public interface TasksService extends IService<TaskEntity> {
TaskInfoResponse showTask(String taskId);
TasksListInfoResponse taskList();
String addTask(TaskRequest taskRequest);
TaskInfoResponse getTask(String taskId);
void updateTask(String taskId, TaskRequest updatedTask);
}
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.guo.learningprogresstracker.dto.PriorityDto;
import com.guo.learningprogresstracker.dto.request.TaskRequest;
import com.guo.learningprogresstracker.dto.response.TaskInfoResponse;
import com.guo.learningprogresstracker.dto.response.TasksListInfoResponse;
import com.guo.learningprogresstracker.entity.TaskEntity;
import com.guo.learningprogresstracker.mapStruct.RequestConvert;
import com.guo.learningprogresstracker.mapStruct.TaskConvert;
@@ -26,9 +27,10 @@ public class TasksServiceImpl extends ServiceImpl<TasksMapper, TaskEntity>
@Override
public TaskInfoResponse showTask(String taskId) {
public TasksListInfoResponse taskList() {
TasksListInfoResponse result = new TasksListInfoResponse();
TaskInfoResponse result = new TaskInfoResponse();
return result;
}
@@ -41,6 +43,21 @@ public class TasksServiceImpl extends ServiceImpl<TasksMapper, TaskEntity>
this.save(task);
return task.getTaskNum();
}
@Override
public TaskInfoResponse getTask(String taskId) {
TaskEntity taskEntity = this.getById(taskId);
TaskInfoResponse taskInfoResponse = TaskConvert.MAPPER.taskEntityToTaskInfoResponse(taskEntity);
return taskInfoResponse;
}
@Override
public void updateTask(String taskId, TaskRequest updatedTask) {
TaskEntity taskEntity = TaskConvert.MAPPER.taskRequestToTaskEntity(updatedTask);
this.updateById(taskEntity);
}
}
@@ -1,34 +1,70 @@
package com.guo.learningprogresstracker.controller;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.guo.learningprogresstracker.dto.request.TaskRequest;
import com.guo.learningprogresstracker.dto.response.TaskInfoResponse;
import com.guo.learningprogresstracker.entity.CommonResult;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import com.guo.learningprogresstracker.entity.TaskEntity;
import com.guo.learningprogresstracker.service.impl.TasksServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import static org.junit.jupiter.api.Assertions.*;
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.verify;
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;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
@SpringBootTest
@AutoConfigureMockMvc
class TaskControllerTest {
@Autowired
private TaskController taskController;
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper jacksonObjectMapper;
@Autowired
private TasksServiceImpl tasksServiceImpl;
@BeforeEach
void setUp() {
StpUtil.login("测试用户", "test_driver");
}
@Test
void addTask() {
void addTask() throws Exception {
TaskRequest taskRequest = getTaskRequest();
String s = jacksonObjectMapper.writeValueAsString(taskRequest);
System.out.println("Token Value: " + StpUtil.getTokenValue());
mockMvc.perform(post("/tasks")
.header("satoken", StpUtil.getTokenValue())
.contentType(MediaType.APPLICATION_JSON)
.content(s))
.andDo(print())
.andExpect(jsonPath("$.code").value(200));
}
private static TaskRequest getTaskRequest() {
TaskRequest taskRequest = mock(TaskRequest.class);
when(taskRequest.getId()).thenReturn(null);
// 设置学习任务的名称
when(taskRequest.getTaskName()).thenReturn("测试任务名称");
@@ -45,37 +81,57 @@ class TaskControllerTest {
when(taskRequest.getContentDifficulty()).thenReturn(4);
// 设置任务的未来价值
when(taskRequest.getFutureValue()).thenReturn(5);
when(taskRequest.getFutureValue()).thenReturn(4);
// 设置用户对任务的主观优先级
when(taskRequest.getSubjectivePriority()).thenReturn(2);
CommonResult commonResult = taskController.addTask(taskRequest);
// 验证 addTask 是否被调用一次
assertEquals(commonResult.getCode(), 200,"测试数据插入失败");
when(taskRequest.getSubjectivePriority()).thenReturn(1);
return taskRequest;
}
/**
* 测试获取全部数据
* @throws Exception
*/
@Test
void showTask() {
String taskId = mock(String.class);
TaskInfoResponse mockData = mock(TaskInfoResponse.class);
void getTask() throws Exception {
String taskId = "1";
mockMvc.perform(get("/tasks/"+taskId)
.header("satoken",StpUtil.getTokenValue()))
.andDo(print())
.andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.data").exists());
CommonResult commonResult = taskController.showTask(taskId);
assertEquals(commonResult.getCode(),200,"获取数据失败");
assertNotNull(commonResult.getData(),"返回值中不存在数据");
assertEquals(commonResult.getData(),mockData,"返回值携带数据不正确");
}
@Test
void updateTask() {
TaskRequest taskRequest = mock(TaskRequest.class);
when(taskRequest.getTaskName()).thenReturn("测试数据名称");
when(taskRequest.getMaterialUrl()).thenReturn("http://example.com/material");
CommonResult commonResult = taskController.updateTask("0", taskRequest);
assertEquals(commonResult.getData(), 200, "任务信息更新失败");
}
@Test
void deleteTask() {
void deleteTask() throws Exception {
CommonResult commonResult = taskController.addTask(getTaskRequest());
assertEquals(ObjectUtils.isNotEmpty(commonResult.getMessage()),"任务创建失败");
TaskEntity taskEntity = tasksServiceImpl.getOne(Wrappers.<TaskEntity>lambdaQuery().eq(TaskEntity::getTaskNum, commonResult.getMessage()));
assertNotNull(taskEntity,"未能找到新创建的任务实体");
mockMvc.perform(delete("/tasks/"+taskEntity.getId()))
.andDo(print())
.andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.data").isEmpty());
}
@Test
void listTasks() {
void listTasks() throws Exception {
mockMvc.perform(get("/tasks")
.header("satoken",StpUtil.getTokenValue()))
.andDo(print())
.andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.data").exists());
}
}