fix: 角色分页缺少 updateTime,重构 mybatis 自动填充处理器

AutoFillMetaObjectHandler 重命名为 MetaFieldFillHandler,移除 handler 子包

添加 spring-boot-starter-webmvc-test 测试依赖
This commit is contained in:
Ray.Hao
2026-08-02 09:16:29 +08:00
parent 27ea1cf114
commit afc1780705
5 changed files with 161 additions and 7 deletions

View File

@@ -106,6 +106,13 @@
<scope>test</scope>
</dependency>
<!-- Spring Boot 4: @AutoConfigureMockMvc 移到了 webmvc-test 模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>

View File

@@ -5,8 +5,8 @@ import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.youlai.boot.framework.mybatis.handler.AutoFillMetaObjectHandler;
import com.youlai.boot.framework.mybatis.interceptor.MyDataPermissionHandler;
import com.youlai.boot.framework.mybatis.MetaFieldFillHandler;
import com.youlai.boot.framework.mybatis.MyDataPermissionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@@ -43,7 +43,7 @@ public class MybatisConfig {
@Bean
public GlobalConfig globalConfig() {
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setMetaObjectHandler(new AutoFillMetaObjectHandler());
globalConfig.setMetaObjectHandler(new MetaFieldFillHandler());
return globalConfig;
}

View File

@@ -1,4 +1,4 @@
package com.youlai.boot.framework.mybatis.handler;
package com.youlai.boot.framework.mybatis;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.youlai.boot.framework.security.util.SecurityUtils;
@@ -17,7 +17,7 @@ import java.time.LocalDateTime;
* @since 3.0.0
*/
@Component
public class AutoFillMetaObjectHandler implements MetaObjectHandler {
public class MetaFieldFillHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {

View File

@@ -1,4 +1,4 @@
package com.youlai.boot.framework.mybatis.interceptor;
package com.youlai.boot.framework.mybatis;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
@@ -23,7 +23,7 @@ import java.lang.reflect.Method;
import java.util.List;
/**
* 数据权限控制
* 数据权限处理
* <p>
* 支持多角色数据权限合并并集策略
* - 如果任一角色是 ALL则跳过数据权限过滤

View File

@@ -0,0 +1,147 @@
package com.youlai.boot;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import static org.junit.jupiter.api.Assertions.*;
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.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* 认证 + 当前用户接口单元测试
*
* @author Ray.Hao
* @since 4.6.0
*/
@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("dev")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class AuthControllerTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private ObjectMapper objectMapper;
private static String accessToken;
@Test
@Order(1)
@DisplayName("登录成功")
void loginSuccess() throws Exception {
String body = """
{
"username": "admin",
"password": "123456"
}
""";
MvcResult result = mockMvc.perform(post("/api/v1/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(body))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value("00000"))
.andExpect(jsonPath("$.data.accessToken").isString())
.andExpect(jsonPath("$.data.refreshToken").isString())
.andExpect(jsonPath("$.data.tokenType").value("Bearer"))
.andExpect(jsonPath("$.data.expiresIn").isNumber())
.andReturn();
JsonNode root = objectMapper.readTree(result.getResponse().getContentAsString());
accessToken = root.path("data").path("accessToken").asText();
assertNotNull(accessToken, "登录后应返回 accessToken");
assertFalse(accessToken.isBlank(), "accessToken 不能为空");
}
@Test
@Order(2)
@DisplayName("密码错误登录失败")
void loginWithWrongPassword() throws Exception {
String body = """
{
"username": "admin",
"password": "wrong_password"
}
""";
mockMvc.perform(post("/api/v1/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(body))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value("A0210"))
.andExpect(jsonPath("$.msg").value("密码错误"));
}
@Test
@Order(3)
@DisplayName("空用户名登录失败")
void loginWithEmptyUsername() throws Exception {
String body = """
{
"username": "",
"password": "123456"
}
""";
mockMvc.perform(post("/api/v1/auth/login")
.contentType(MediaType.APPLICATION_JSON)
.content(body))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value("B0001"));
}
@Test
@Order(4)
@DisplayName("登录后获取当前用户信息")
void getCurrentUserWithToken() throws Exception {
assertNotNull(accessToken, "accessToken 应由登录测试先行填充");
mockMvc.perform(get("/api/v1/users/me")
.header("Authorization", "Bearer " + accessToken))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value("00000"))
.andExpect(jsonPath("$.data.userId").isNumber())
.andExpect(jsonPath("$.data.username").isString())
.andExpect(jsonPath("$.data.nickname").isString())
.andExpect(jsonPath("$.data.roles").isArray())
.andExpect(jsonPath("$.data.perms").isArray());
}
@Test
@Order(5)
@DisplayName("无 Token 请求 /me 返回令牌无效")
void getCurrentUserWithoutToken() throws Exception {
mockMvc.perform(get("/api/v1/users/me"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value("A0230"))
.andExpect(jsonPath("$.msg").value("令牌无效或已过期"));
}
@Test
@Order(6)
@DisplayName("伪造 Token 请求 /me 返回令牌无效")
void getCurrentUserWithInvalidToken() throws Exception {
mockMvc.perform(get("/api/v1/users/me")
.header("Authorization", "Bearer invalid_token_xxx"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value("A0230"));
}
}