feat(admin): 管理面信封化+分页化,OpenAPI 文档端点旁路(B1-B6)
build / build-push (push) Successful in 1m59s

- pom: 引 yh-platform-dependencies(import) / yh-api-std / springdoc-openapi-ui 1.7.0 + nexus-public 仓库
- RoutingFilter.isGatewayInternal: /v3/api-docs、/swagger-ui/**、/swagger-ui.html 纳入自身路径旁路,避免被 TargetRouter 当 target 裸 404
- InternalAdminController: page/routes/targets/logs 三接口分页化返回 BasePageResult;补 @Operation(operationId=GatewayAdmin.*);错误契约去 @ResponseStatus(400) 改 200+code=2;logs 兼容旧 limit 入参
- RouteRefreshController: 补 @Tag/@Operation(operationId=GatewayAdmin.refresh)
- static/admin.html: 适配 {code,msg,data} 信封与 content/total 分页
- InternalAdminControllerTest: 重写 12 用例,77 项测试全绿
This commit is contained in:
chenxing
2026-09-23 12:17:02 +08:00
parent 6e64acc0c7
commit 2389d2fa9a
6 changed files with 198 additions and 67 deletions
@@ -15,7 +15,6 @@ import org.springframework.data.domain.Pageable;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import java.util.ArrayList;
import java.util.Arrays;
import static org.mockito.ArgumentMatchers.any;
@@ -30,7 +29,12 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
/**
* 治理小页后端(N02 TDD):page/routes/targets/logs 查询形态 + 路由/目标行内编辑的
* 值域闸(非法值 400 且不落库)+ 写后热重载调用。MockMvc standalone + mock 仓储/注册表。
* 值域闸(非法值不落库)+ 写后热重载调用。MockMvc standalone + mock 仓储/注册表。
* <p>
* <b>契约说明(B3/B4 后)</b>:查询接口返回 {@code BasePageResult}({@code {total, content, ...}}),
* 非法入参返回 <b>HTTP 200</b> + {@code {code:2, msg}}(平台规范)。standalone 模式不装配
* {@code yh-api-std} 的 {@code ApiResponseAdvice},故成功响应在本测试中保持裸结构;
* 线上由 advice 统一再包一层 {@code {code,msg,data}}(分页体落在 {@code data})。
*/
class InternalAdminControllerTest {
@@ -51,8 +55,10 @@ class InternalAdminControllerTest {
controller.setRepos(routeRepo, targetRepo, logRepo, registry);
mvc = MockMvcBuilders.standaloneSetup(controller).build();
when(routeRepo.findAll()).thenReturn(new ArrayList<>(Arrays.asList(route(1L))));
when(targetRepo.findAll()).thenReturn(new ArrayList<>(Arrays.asList(target(5L))));
when(routeRepo.findAll(any(Pageable.class)))
.thenReturn(new PageImpl<>(Arrays.asList(route(1L))));
when(targetRepo.findAll(any(Pageable.class)))
.thenReturn(new PageImpl<>(Arrays.asList(target(5L))));
when(registry.reload()).thenReturn(22);
when(registry.reloadTargets()).thenReturn(13);
when(logRepo.pageLatest(any(), any(), any(Pageable.class)))
@@ -73,8 +79,19 @@ class InternalAdminControllerTest {
void routes_listSorted() throws Exception {
mvc.perform(get("/api/internal/admin/routes"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].targetCode").value("userbase"))
.andExpect(jsonPath("$[0].pathPattern").value("/oapi/user/**"));
.andExpect(jsonPath("$.total").value(1))
.andExpect(jsonPath("$.pageIndex").value(1))
.andExpect(jsonPath("$.content[0].targetCode").value("userbase"))
.andExpect(jsonPath("$.content[0].pathPattern").value("/oapi/user/**"));
}
@Test
void targets_paged() throws Exception {
mvc.perform(get("/api/internal/admin/targets?pageIndex=1&pageSize=20"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.total").value(1))
.andExpect(jsonPath("$.content[0].targetCode").value("notice"));
verify(targetRepo).findAll(Mockito.<Pageable>argThat(p -> p.getPageSize() == 20 && p.getPageNumber() == 0));
}
@Test
@@ -90,34 +107,37 @@ class InternalAdminControllerTest {
}
@Test
void routeUpdate_illegalAuthMode_400AndNoSave() throws Exception {
void routeUpdate_illegalAuthMode_bizErrorAndNoSave() throws Exception {
when(routeRepo.findById(1L)).thenReturn(java.util.Optional.of(route(1L)));
mvc.perform(put("/api/internal/admin/routes/1")
.contentType("application/json")
.content("{\"authMode\":\"OPEN\"}"))
.andExpect(status().isBadRequest())
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(2))
.andExpect(jsonPath("$.msg").value(org.hamcrest.Matchers.containsString("authMode")));
verify(routeRepo, never()).save(any());
verify(registry, never()).reload();
}
@Test
void routeUpdate_negativeTtl_400() throws Exception {
void routeUpdate_negativeTtl_bizError() throws Exception {
when(routeRepo.findById(1L)).thenReturn(java.util.Optional.of(route(1L)));
mvc.perform(put("/api/internal/admin/routes/1")
.contentType("application/json")
.content("{\"cacheTtlSeconds\":-1}"))
.andExpect(status().isBadRequest());
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(2));
verify(routeRepo, never()).save(any());
}
@Test
void routeUpdate_missingRow_400() throws Exception {
void routeUpdate_missingRow_bizError() throws Exception {
when(routeRepo.findById(99L)).thenReturn(java.util.Optional.empty());
mvc.perform(put("/api/internal/admin/routes/99")
.contentType("application/json")
.content("{\"enabled\":false}"))
.andExpect(status().isBadRequest());
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(2));
}
@Test
@@ -133,12 +153,13 @@ class InternalAdminControllerTest {
}
@Test
void targetUpdate_blankBaseUrl_400NoSave() throws Exception {
void targetUpdate_blankBaseUrl_bizErrorNoSave() throws Exception {
when(targetRepo.findById(5L)).thenReturn(java.util.Optional.of(target(5L)));
mvc.perform(put("/api/internal/admin/targets/5")
.contentType("application/json")
.content("{\"baseUrl\":\" \"}"))
.andExpect(status().isBadRequest());
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(2));
verify(targetRepo, never()).save(any());
}
@@ -147,10 +168,18 @@ class InternalAdminControllerTest {
mvc.perform(get("/api/internal/admin/logs?targetCode=notice&callerAppKey=100001&limit=10"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.total").value(1))
.andExpect(jsonPath("$.rows[0].targetCode").value("notice"));
.andExpect(jsonPath("$.content[0].targetCode").value("notice"));
verify(logRepo).pageLatest(eq("notice"), eq("100001"), any(Pageable.class));
}
@Test
void logs_pageIndexAndSize() throws Exception {
mvc.perform(get("/api/internal/admin/logs?pageIndex=2&pageSize=20"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.total").value(1));
verify(logRepo).pageLatest(any(), any(), Mockito.argThat(p -> p.getPageSize() == 20 && p.getPageNumber() == 1));
}
@Test
void logs_overLimitCappedTo500() throws Exception {
mvc.perform(get("/api/internal/admin/logs?limit=9999"))