完善查询函数

This commit is contained in:
2025-10-14 12:34:12 +08:00
parent 4940e3a9d2
commit 6e9e7af780
3 changed files with 120 additions and 102 deletions

View File

View File

@@ -1,7 +1,7 @@
'use server' 'use server'
import { Page, Res } from '@/lib/api' import { Page, Res } from '@/lib/api'
import drizzle, { change, cityhash, count, desc, edge, eq, gateway, is, sql, token } from '@/lib/drizzle' import drizzle, { and, change, cityhash, count, desc, edge, eq, gateway, is, sql, token } from '@/lib/drizzle'
export type AllocationStatus = { export type AllocationStatus = {
city: string city: string
@@ -134,7 +134,13 @@ export async function getGatewayConfig(page?: number, filters?: {
.from(gateway) .from(gateway)
.leftJoin(cityhash, eq(cityhash.hash, gateway.cityhash)) .leftJoin(cityhash, eq(cityhash.hash, gateway.cityhash))
.leftJoin(edge, eq(edge.macaddr, gateway.edge)) .leftJoin(edge, eq(edge.macaddr, gateway.edge))
.where(filters?.mac ? eq(gateway.macaddr, filters?.mac) : undefined) .where(filters ? and(
filters.mac ? eq(gateway.macaddr, filters.mac) : undefined,
filters.public ? eq(edge.public, filters.public) : undefined,
filters.city ? eq(cityhash.city, filters.city) : undefined,
filters.user ? eq(gateway.user, filters.user) : undefined,
filters.inner_ip ? eq(gateway.network, filters.inner_ip) : undefined,
) : undefined)
.orderBy(sql`inet_aton(gateway.inner_ip)`) .orderBy(sql`inet_aton(gateway.inner_ip)`)
.offset((page - 1) * 250) .offset((page - 1) * 250)
.limit(250), .limit(250),
@@ -210,7 +216,7 @@ export async function getCityNodeCount() {
export type Edge = { export type Edge = {
id: number id: number
macaddr: string macaddr: string
city: string city: string | null
public: string public: string
isp: string isp: string
single: number | boolean single: number | boolean
@@ -220,19 +226,49 @@ export type Edge = {
} }
// 获取节点信息 // 获取节点信息
export async function getEdgeNodes(page: number, size: number, props?: { export async function getEdgeNodes(page: number, size: number, filters?: {
macaddr?: string macaddr?: string
public?: string public?: string
city?: string city?: string
isp?: string isp?: string
}, }): Promise<Res<Page<Edge>>> {
) {
try { try {
const offset = Math.max(0, (page - 1)) * size page = Math.max(1, page)
const limit = Math.min(100, Math.max(10, size)) size = Math.min(100, Math.max(10, size))
const [total, data] = await Promise.all([ const condition = and(
drizzle.$count(edge, eq(edge.active, 1)), eq(edge.active, 1),
filters?.macaddr ? eq(edge.macaddr, filters.macaddr) : undefined,
filters?.public ? eq(edge.public, filters.public) : undefined,
filters?.city ? eq(cityhash.city, filters.city) : undefined,
filters?.isp ? eq(edge.isp, filters.isp) : undefined,
)
console.log(drizzle
.select({
id: edge.id,
macaddr: edge.macaddr,
city: cityhash.city,
public: edge.public,
isp: edge.isp,
single: edge.single,
sole: edge.sole,
arch: edge.arch,
online: edge.online,
})
.from(edge)
.leftJoin(cityhash, eq(cityhash.id, edge.cityId))
.where(condition)
.orderBy(edge.id)
.offset(page * size - size)
.limit(size).toSQL().sql)
const [total, items] = await Promise.all([
drizzle
.select({ value: count() })
.from(edge)
.leftJoin(cityhash, eq(cityhash.id, edge.cityId))
.where(condition),
drizzle drizzle
.select({ .select({
id: edge.id, id: edge.id,
@@ -247,25 +283,26 @@ export async function getEdgeNodes(page: number, size: number, props?: {
}) })
.from(edge) .from(edge)
.leftJoin(cityhash, eq(cityhash.id, edge.cityId)) .leftJoin(cityhash, eq(cityhash.id, edge.cityId))
.where(eq(edge.active, 1)) .where(condition)
.orderBy(edge.id) .orderBy(edge.id)
.offset(offset) .offset(page * size - size)
.limit(limit), .limit(size),
]) ])
return { return {
data, success: true,
totalCount: total, data: {
currentPage: Math.floor(offset / limit) + 1, total: total[0].value,
totalPages: Math.ceil(total / limit), page,
size,
items,
},
} }
} }
catch (error) { catch (error) {
console.error('Edge nodes query error:', error) console.error('Edge nodes query error:', error)
return { return {
success: false, success: false,
data: [],
error: '查询边缘节点失败', error: '查询边缘节点失败',
} }
} }

View File

@@ -8,16 +8,16 @@ import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form' import { useForm } from 'react-hook-form'
import { z } from 'zod' import { z } from 'zod'
import { Button } from '@/components/ui/button' import { Button } from '@/components/ui/button'
import { Form, FormField, FormItem, FormLabel } from '@/components/ui/form' import { Form, FormControl, FormField, FormItem, FormLabel } from '@/components/ui/form'
import { Input } from '@/components/ui/input' import { Input } from '@/components/ui/input'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
// 定义表单验证规则 // 定义表单验证规则
const filterSchema = z.object({ const filterSchema = z.object({
macaddr: z.string().optional(), macaddr: z.string(),
public: z.string().optional(), public: z.string(),
city: z.string().optional(), city: z.string(),
isp: z.string().optional(), isp: z.string(),
}) })
type FilterFormValues = z.infer<typeof filterSchema> type FilterFormValues = z.infer<typeof filterSchema>
@@ -28,9 +28,9 @@ export default function Edge() {
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
// 分页状态 // 分页状态
const [currentPage, setCurrentPage] = useState(1) const [page, setPage] = useState(1)
const [itemsPerPage, setItemsPerPage] = useState(100) // 默认100条 const [size, setSize] = useState(100)
const [totalItems, setTotalItems] = useState(0) const [total, setTotal] = useState(0)
// 初始化表单 // 初始化表单
const form = useForm<FilterFormValues>({ const form = useForm<FilterFormValues>({
@@ -43,57 +43,53 @@ export default function Edge() {
}, },
}) })
useEffect(() => { const fetchData = async (page: number, size: number) => {
fetchData({}) const filters = form.getValues()
}, [currentPage, itemsPerPage])
const fetchData = async (val: {
macaddr?: string
public?: string
city?: string
isp?: string
}) => {
try {
setError(null)
setLoading(true) setLoading(true)
try {
const result = await getEdgeNodes(page, size, filters)
if (!result.success) {
throw new Error(result.error)
}
// 计算偏移量 const data = result.data
const offset = (currentPage - 1) * itemsPerPage console.log(data)
const result = await getEdgeNodes(offset, itemsPerPage, val) setData(data.items)
const validatedData = (result.data).map(item => ({ setTotal(data.total)
id: item.id, setPage(data.page)
macaddr: item.macaddr || '', setSize(data.size)
city: item.city || '',
public: item.public || '',
isp: item.isp || '',
single: item.single,
sole: item.sole,
arch: item.arch,
online: item.online,
}))
setData(validatedData) setError(null)
setTotalItems(result.totalCount || 0)
} }
catch (error) { catch (error) {
console.error('Failed to fetch edge nodes:', error) setError('获取边缘节点数据失败' + (error instanceof Error ? `: ${error.message}` : ''))
setError(error instanceof Error ? error.message : '获取边缘节点数据失败')
} }
finally { finally {
setLoading(false) setLoading(false)
} }
} }
const onSubmit = async (values: FilterFormValues) => { const onSubmit = () => {
setCurrentPage(1) fetchData(page, size)
const filters = {
macaddr: values.macaddr || undefined,
public: values.public || undefined,
city: values.city || undefined,
isp: values.isp || undefined,
} }
fetchData(filters) const onReset = () => {
form.reset()
fetchData(page, size)
}
// 处理页码变化
const handlePageChange = (page: number) => {
setPage(page)
fetchData(page, size)
}
// 处理每页显示数量变化
const handleSizeChange = (size: number) => {
setPage(1)
setSize(size)
fetchData(1, size)
} }
// 多IP节点格式化 // 多IP节点格式化
@@ -165,32 +161,9 @@ export default function Edge() {
return `${Math.floor(seconds / 86400)}` return `${Math.floor(seconds / 86400)}`
} }
// 处理页码变化 useEffect(() => {
const handlePageChange = (page: number) => { fetchData(page, size)
setCurrentPage(page) }, [])
const formValues = form.getValues()
const filters = {
macaddr: formValues.macaddr || '',
public: formValues.public || '',
city: formValues.city || '',
isp: formValues.isp || '',
}
fetchData(filters)
}
// 处理每页显示数量变化
const handleSizeChange = (size: number) => {
setItemsPerPage(size)
setCurrentPage(1)
const formValues = form.getValues()
const filters = {
macaddr: formValues.macaddr || '',
public: formValues.public || '',
city: formValues.city || '',
isp: formValues.isp || '',
}
fetchData(filters)
}
if (loading) return ( if (loading) return (
<div className="bg-white shadow p-6"> <div className="bg-white shadow p-6">
@@ -204,7 +177,7 @@ export default function Edge() {
<h2 className="text-xl font-semibold text-gray-800 mb-4"></h2> <h2 className="text-xl font-semibold text-gray-800 mb-4"></h2>
<div className="text-center py-8 text-red-600">{error}</div> <div className="text-center py-8 text-red-600">{error}</div>
<button <button
onClick={() => fetchData({})} onClick={() => fetchData(page, size)}
className="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors mx-auto block" className="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors mx-auto block"
> >
@@ -220,11 +193,14 @@ export default function Edge() {
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4"> <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<div className="flex gap-4"> <div className="flex gap-4">
<FormField <FormField
control={form.control}
name="macaddr" name="macaddr"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>MAC地址</FormLabel> <FormLabel>MAC地址</FormLabel>
<FormControl>
<Input placeholder="输入MAC地址" {...field} /> <Input placeholder="输入MAC地址" {...field} />
</FormControl>
</FormItem> </FormItem>
)} )}
/> />
@@ -233,7 +209,9 @@ export default function Edge() {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>IP</FormLabel> <FormLabel>IP</FormLabel>
<FormControl>
<Input placeholder="输入公网IP" {...field} /> <Input placeholder="输入公网IP" {...field} />
</FormControl>
</FormItem> </FormItem>
)} )}
/> />
@@ -242,7 +220,9 @@ export default function Edge() {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel></FormLabel> <FormLabel></FormLabel>
<FormControl>
<Input placeholder="输入城市名称" {...field} /> <Input placeholder="输入城市名称" {...field} />
</FormControl>
</FormItem> </FormItem>
)} )}
/> />
@@ -252,14 +232,16 @@ export default function Edge() {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel></FormLabel> <FormLabel></FormLabel>
<FormControl>
<Input placeholder="输入运营商" {...field} /> <Input placeholder="输入运营商" {...field} />
</FormControl>
</FormItem> </FormItem>
)} )}
/> />
<Button type="submit" className="mt-5 py-2 bg-blue-600 hover:bg-blue-700"> <Button type="submit" className="mt-5 py-2 bg-blue-600 hover:bg-blue-700">
</Button> </Button>
<Button type="button" variant="outline" className="mt-5 py-2" onClick={() => form.reset()}> <Button type="button" variant="outline" className="mt-5 py-2" onClick={onReset}>
</Button> </Button>
</div> </div>
@@ -333,12 +315,11 @@ export default function Edge() {
{/* 分页 */} {/* 分页 */}
<Pagination <Pagination
page={currentPage} page={page}
size={itemsPerPage} size={size}
total={totalItems} total={total}
onPageChange={handlePageChange} onPageChange={handlePageChange}
onSizeChange={handleSizeChange} onSizeChange={handleSizeChange}
className="mt-4"
/> />
</> </>
)} )}