[功能] 调整访问日志分页大小为20,并更新相关组件以支持动态分页

This commit is contained in:
ryan
2026-03-18 09:52:39 +08:00
parent 70da7c772c
commit 2e875f583b
6 changed files with 87 additions and 7 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
.cache
.idea
.vscode
upload
+8
View File
@@ -34,6 +34,14 @@ func ListNodes() (nodes []*Node, err error) {
return nodes, err
}
func ListNodesByNodeIDs(nodeIDs []string) (nodes []*Node, err error) {
if len(nodeIDs) == 0 {
return []*Node{}, nil
}
err = DB.Where("node_id IN ?", nodeIDs).Find(&nodes).Error
return nodes, err
}
func GetNodeByNodeID(nodeID string) (*Node, error) {
node := &Node{}
err := DB.Where("node_id = ?", nodeID).First(node).Error
+14 -2
View File
@@ -7,7 +7,7 @@ import (
)
const (
defaultAccessLogPageSize = 50
defaultAccessLogPageSize = 20
maxAccessLogPageSize = 200
)
@@ -51,7 +51,19 @@ func ListAccessLogs(nodeID string, page int, pageSize int) (*AccessLogList, erro
if err != nil {
return nil, err
}
nodes, err := model.ListNodes()
nodeIDs := make([]string, 0, len(logs))
seenNodeIDs := make(map[string]struct{}, len(logs))
for _, item := range logs {
if item == nil {
continue
}
if _, exists := seenNodeIDs[item.NodeID]; exists {
continue
}
seenNodeIDs[item.NodeID] = struct{}{}
nodeIDs = append(nodeIDs, item.NodeID)
}
nodes, err := model.ListNodesByNodeIDs(nodeIDs)
if err != nil {
return nil, err
}
@@ -98,3 +98,44 @@ func TestListAccessLogsIncludesSummaryTotals(t *testing.T) {
t.Fatalf("expected filtered items=2, got %d", len(filtered.Items))
}
}
func TestListAccessLogsUsesDefaultPageSize(t *testing.T) {
setupServiceTestDB(t)
now := time.Now()
if err := model.DB.Create(&model.Node{
NodeID: "node-default-page-size",
Name: "edge-default-page-size",
}).Error; err != nil {
t.Fatalf("failed to seed node: %v", err)
}
logs := make([]*model.NodeAccessLog, 0, 25)
for index := range 25 {
logs = append(logs, &model.NodeAccessLog{
NodeID: "node-default-page-size",
LoggedAt: now.Add(-time.Duration(index) * time.Minute),
RemoteAddr: "1.1.1.1",
Host: "example.com",
Path: "/default-page-size",
StatusCode: 200,
})
}
if err := model.DB.Create(&logs).Error; err != nil {
t.Fatalf("failed to seed access logs: %v", err)
}
result, err := ListAccessLogs("", 0, 0)
if err != nil {
t.Fatalf("ListAccessLogs failed: %v", err)
}
if result.PageSize != 20 {
t.Fatalf("expected default page_size=20, got %d", result.PageSize)
}
if len(result.Items) != 20 {
t.Fatalf("expected current page items=20, got %d", len(result.Items))
}
if !result.HasMore {
t.Fatal("expected has_more to be true")
}
}
@@ -2,7 +2,7 @@ import { apiRequest } from '@/lib/api/client';
import type { AccessLogList } from '@/features/access-logs/types';
export function getAccessLogs(page: number, nodeId?: string, pageSize = 50) {
export function getAccessLogs(page: number, nodeId?: string, pageSize = 20) {
const normalizedNodeId = nodeId?.trim();
const searchParams = new URLSearchParams({
p: String(Math.max(page, 0)),
@@ -15,11 +15,14 @@ import type { AccessLogItem } from '@/features/access-logs/types';
import {
PrimaryButton,
ResourceInput,
ResourceSelect,
SecondaryButton,
} from '@/features/shared/components/resource-primitives';
import { formatDateTime, formatRelativeTime } from '@/lib/utils/date';
const accessLogsQueryKey = (nodeId: string) => ['access-logs', nodeId] as const;
const defaultPageSize = 20;
const accessLogPageSizeOptions = [20, 50, 100, 200];
function getErrorMessage(error: unknown) {
return error instanceof Error ? error.message : '请求失败,请稍后重试。';
@@ -47,15 +50,16 @@ export function AccessLogsPage() {
const [nodeFilterInput, setNodeFilterInput] = useState('');
const [nodeFilter, setNodeFilter] = useState('');
const [page, setPage] = useState(0);
const [pageSize, setPageSize] = useState(defaultPageSize);
const logsQuery = useQuery({
queryKey: [...accessLogsQueryKey(nodeFilter), page],
queryFn: () => getAccessLogs(page, nodeFilter),
queryKey: [...accessLogsQueryKey(nodeFilter), page, pageSize],
queryFn: () => getAccessLogs(page, nodeFilter, pageSize),
placeholderData: (previousData) => previousData,
});
const logs = useMemo(() => logsQuery.data?.items ?? [], [logsQuery.data]);
const hasMore = logsQuery.data?.has_more ?? false;
const pageSize = logsQuery.data?.page_size ?? 50;
const summary = useMemo(
() =>
buildSummary(
@@ -125,6 +129,21 @@ export function AccessLogsPage() {
placeholder="输入 node_id 过滤访问日志"
className="lg:max-w-md"
/>
<ResourceSelect
value={String(pageSize)}
onChange={(event) => {
setPageSize(Number(event.target.value) || defaultPageSize);
setPage(0);
}}
className="lg:w-40"
aria-label="每页条数"
>
{accessLogPageSizeOptions.map((option) => (
<option key={option} value={option}>
每页 {option} 条
</option>
))}
</ResourceSelect>
<div className="flex flex-wrap gap-2">
<PrimaryButton
type="button"