From abfaa8c2d10ad0dd466a403ae9557c6a1cef9c11 Mon Sep 17 00:00:00 2001 From: xiaoqu Date: Thu, 3 Sep 2026 18:27:35 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E7=94=A8=E6=88=B7=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E6=94=B9=E6=9C=8D=E5=8A=A1=E7=AB=AF=E5=88=86=E9=A1=B5?= =?UTF-8?q?(page/pageSize/total,=20pageSize=E4=B8=8A=E9=99=90100),=20?= =?UTF-8?q?=E5=89=8D=E7=AB=AF=E5=8A=A0=E5=88=86=E9=A1=B5=E5=99=A8;=20MCP?= =?UTF-8?q?=E5=B7=A5=E5=85=B7rows=E4=B8=8A=E9=99=90100=E5=B7=B2=E6=9C=89,?= =?UTF-8?q?=20=E8=A1=A5=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xiaoqu-agent-service/front/src/App.vue | 20 ++++-- xiaoqu-agent-service/front/src/style.css | 1 + .../agent/admin/AdminConsoleController.java | 16 +++-- .../admin/AdminConsoleControllerTest.java | 65 +++++++++++++++++++ 4 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 xiaoqu-agent-service/src/test/java/me/iiv/agent/admin/AdminConsoleControllerTest.java diff --git a/xiaoqu-agent-service/front/src/App.vue b/xiaoqu-agent-service/front/src/App.vue index 635d0b5..8ea33bc 100644 --- a/xiaoqu-agent-service/front/src/App.vue +++ b/xiaoqu-agent-service/front/src/App.vue @@ -16,6 +16,7 @@ const session = reactive({ name: '', csrfToken: '' }) const loginForm = reactive({ account: '', password: '' }) const users = ref([]) const userKeyword = ref('') +const userPage = reactive({ page: 1, pageSize: 20, total: 0 }) const selectedUser = ref(null) const permission = ref(null) const tokens = ref([]) @@ -80,14 +81,22 @@ async function switchMenu(id) { if (id === 'audit') await Promise.all([loadAudit(), api('/tools').then(data => { tools.value = data })]) } -async function loadUsers() { - users.value = await api(`/users${qs({ keyword: userKeyword.value })}`) +async function loadUsers(resetPage = false) { + if (resetPage) userPage.page = 1 + const data = await api(`/users${qs({ keyword: userKeyword.value, page: userPage.page, pageSize: userPage.pageSize })}`) + users.value = data.records + userPage.total = data.total const existing = users.value.find(user => user.id === selectedUser.value?.id) if (existing) selectedUser.value = existing else if (users.value.length) await selectUser(users.value[0]) else { selectedUser.value = null; permission.value = null; tokens.value = [] } } +async function changeUserPage(page) { + userPage.page = page + await run(loadUsers) +} + async function selectUser(user) { selectedUser.value = user ;[permission.value, tokens.value, scopes.value] = await Promise.all([ @@ -248,8 +257,8 @@ onMounted(async () => {
人员来自现有 user 表
- 搜索 + data-testid="admin-input-user-search" @keyup.enter="run(() => loadUsers(true))" /> + 搜索
+
数据权限归属只读 · 在现有系统维护
diff --git a/xiaoqu-agent-service/front/src/style.css b/xiaoqu-agent-service/front/src/style.css index a759b30..1244039 100644 --- a/xiaoqu-agent-service/front/src/style.css +++ b/xiaoqu-agent-service/front/src/style.css @@ -40,6 +40,7 @@ button, input { font: inherit; } .user-row.selected { background: #ecf5ff; } .user-row span:first-child { display: flex; flex-direction: column; gap: 4px; } .user-row small { color: #909399; } +.user-pager { margin-top: 10px; justify-content: flex-end; } .permission-card dl { margin: 0; border-top: 1px solid #ebeef5; } .permission-card dl > div { display: grid; grid-template-columns: 92px 1fr; border-bottom: 1px solid #ebeef5; } .permission-card dt, .permission-card dd { margin: 0; padding: 10px 6px; } diff --git a/xiaoqu-agent-service/src/main/java/me/iiv/agent/admin/AdminConsoleController.java b/xiaoqu-agent-service/src/main/java/me/iiv/agent/admin/AdminConsoleController.java index 50ad2fa..a059811 100644 --- a/xiaoqu-agent-service/src/main/java/me/iiv/agent/admin/AdminConsoleController.java +++ b/xiaoqu-agent-service/src/main/java/me/iiv/agent/admin/AdminConsoleController.java @@ -82,19 +82,26 @@ public class AdminConsoleController { } @GetMapping("/users") - public List users(@RequestParam(defaultValue = "") String keyword) { + public UserPageView users(@RequestParam(defaultValue = "") String keyword, + @RequestParam(defaultValue = "1") int page, + @RequestParam(defaultValue = "20") int pageSize) { if (keyword.length() > 100) bad("搜索关键词最多 100 字"); + int pageNo = Math.max(page, 1); + int size = pageSize < 1 ? 20 : Math.min(pageSize, 100); LambdaQueryWrapper query = new LambdaQueryWrapper().eq(User::getSoftDelete, 0); if (!keyword.isBlank()) query.and(w -> w.like(User::getName, keyword.trim()) .or().like(User::getMobile, keyword.trim())); - List users = userMapper.selectList(query.orderByDesc(User::getId).last("LIMIT 100")); + Long total = userMapper.selectCount(query); + List users = userMapper.selectList(query.orderByDesc(User::getId) + .last("LIMIT " + (long) (pageNo - 1) * size + "," + size)); Set ids = users.stream().map(User::getId).collect(Collectors.toSet()); Map counts = ids.isEmpty() ? Map.of() : tokenMapper.selectList( new LambdaQueryWrapper().in(AgentApiKey::getUserId, ids) .eq(AgentApiKey::getSoftDelete, 0)) .stream().collect(Collectors.groupingBy(AgentApiKey::getUserId, Collectors.counting())); - return users.stream().map(user -> new UserView(user.getId(), user.getName(), maskMobile(user.getMobile()), - counts.getOrDefault(user.getId(), 0L))).toList(); + List records = users.stream().map(user -> new UserView(user.getId(), user.getName(), + maskMobile(user.getMobile()), counts.getOrDefault(user.getId(), 0L))).toList(); + return new UserPageView(total, pageNo, size, records); } @GetMapping("/users/{id}/permission") @@ -375,6 +382,7 @@ public class AdminConsoleController { } public record UserView(Long id, String name, String mobile, long tokenCount) {} + public record UserPageView(long total, int page, int pageSize, List records) {} public record PermissionView(String authorityName, Integer isAgent, String agentName, Integer enterpriseType, String visibleRange) {} public record TokenRequest(String memo, List scopeIds, Date expireTime) {} diff --git a/xiaoqu-agent-service/src/test/java/me/iiv/agent/admin/AdminConsoleControllerTest.java b/xiaoqu-agent-service/src/test/java/me/iiv/agent/admin/AdminConsoleControllerTest.java new file mode 100644 index 0000000..ccbf91d --- /dev/null +++ b/xiaoqu-agent-service/src/test/java/me/iiv/agent/admin/AdminConsoleControllerTest.java @@ -0,0 +1,65 @@ +package me.iiv.agent.admin; + +import me.iiv.agent.mapper.m0.AgentMapper; +import me.iiv.agent.mapper.m0.AuthorityMapper; +import me.iiv.agent.mapper.m0.UserMapper; +import me.iiv.agent.mapper.svc.AgentApiKeyMapper; +import me.iiv.agent.mapper.svc.AgentScopeMapper; +import me.iiv.agent.mapper.svc.AuditLogMapper; +import me.iiv.agent.mapper.svc.ScopeToolMapper; +import me.iiv.agent.mapper.svc.TokenScopeMapper; +import me.iiv.agent.mapper.svc.ToolMapper; +import me.iiv.agent.permission.PermissionService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class AdminConsoleControllerTest { + + @Mock UserMapper userMapper; + @Mock AuthorityMapper authorityMapper; + @Mock AgentMapper agentMapper; + @Mock AgentApiKeyMapper tokenMapper; + @Mock AgentScopeMapper scopeMapper; + @Mock ToolMapper toolMapper; + @Mock ScopeToolMapper scopeToolMapper; + @Mock AuditLogMapper auditMapper; + @Mock TokenScopeMapper tokenScopeMapper; + @Mock PermissionService permissionService; + @Mock AdminTokenService tokenService; + + AdminConsoleController controller; + + @BeforeEach + void setUp() { + controller = new AdminConsoleController(userMapper, authorityMapper, agentMapper, tokenMapper, + scopeMapper, toolMapper, scopeToolMapper, auditMapper, tokenScopeMapper, + permissionService, tokenService); + when(userMapper.selectCount(any())).thenReturn(0L); + when(userMapper.selectList(any())).thenReturn(List.of()); + } + + @Test + void usersClampsPageSizeToHundred() { + AdminConsoleController.UserPageView view = controller.users("", 1, 500); + assertEquals(100, view.pageSize()); + assertEquals(1, view.page()); + assertEquals(0, view.total()); + } + + @Test + void usersDefaultsInvalidPageAndSize() { + AdminConsoleController.UserPageView view = controller.users("", 0, 0); + assertEquals(1, view.page()); + assertEquals(20, view.pageSize()); + } +}