feat: 支持 RPC 节点启用/禁用功能
- 后端:更新 RpcNodeService,确保禁用的节点被忽略 - getAllNodesWithDefault() 只返回启用的节点 - checkAllNodesHealth() 只检查启用的节点 - getAvailableNode() 已正确实现,只查询启用的节点 - 前端:添加启用/禁用开关功能 - 在 RpcNodeSettings 页面添加启用列,使用 Switch 组件 - 添加 handleToggleEnabled 函数处理启用/禁用切换 - 添加多语言支持(中文简体、繁体、英文)
This commit is contained in:
@@ -61,10 +61,12 @@ class RpcNodeService(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有节点配置(包含默认节点,用于内部使用)
|
* 获取所有节点配置(包含默认节点,用于内部使用)
|
||||||
|
* 只返回启用的节点,禁用的节点会被忽略
|
||||||
* 默认节点始终排在最后
|
* 默认节点始终排在最后
|
||||||
*/
|
*/
|
||||||
fun getAllNodesWithDefault(): List<RpcNodeConfig> {
|
fun getAllNodesWithDefault(): List<RpcNodeConfig> {
|
||||||
val allNodes = rpcNodeConfigRepository.findAllByOrderByPriorityAsc()
|
// 只查询启用的节点
|
||||||
|
val allNodes = rpcNodeConfigRepository.findAllByEnabledTrueOrderByPriorityAsc()
|
||||||
|
|
||||||
// 分离默认节点和用户配置的节点
|
// 分离默认节点和用户配置的节点
|
||||||
val (defaultNodes, userNodes) = allNodes.partition { isDefaultNode(it) }
|
val (defaultNodes, userNodes) = allNodes.partition { isDefaultNode(it) }
|
||||||
@@ -366,14 +368,16 @@ class RpcNodeService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量检查所有节点健康状态(不包含默认节点)
|
* 批量检查所有节点健康状态(不包含默认节点和禁用的节点)
|
||||||
* 默认节点作为兜底,不应该返回给前端
|
* 默认节点作为兜底,不应该返回给前端
|
||||||
|
* 禁用的节点不应该被检查
|
||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
fun checkAllNodesHealth(): Result<Map<Long, NodeCheckResult>> {
|
fun checkAllNodesHealth(): Result<Map<Long, NodeCheckResult>> {
|
||||||
return try {
|
return try {
|
||||||
val allNodes = rpcNodeConfigRepository.findAll()
|
// 只查询启用的节点,过滤掉默认节点和禁用的节点
|
||||||
// 过滤掉默认节点,只检查用户配置的节点
|
val allNodes = rpcNodeConfigRepository.findAllByEnabledTrueOrderByPriorityAsc()
|
||||||
|
// 过滤掉默认节点,只检查用户配置的启用节点
|
||||||
val nodes = allNodes.filterNot { isDefaultNode(it) }
|
val nodes = allNodes.filterNot { isDefaultNode(it) }
|
||||||
val results = mutableMapOf<Long, NodeCheckResult>()
|
val results = mutableMapOf<Long, NodeCheckResult>()
|
||||||
|
|
||||||
|
|||||||
@@ -1084,8 +1084,12 @@
|
|||||||
"statusUnhealthy": "Unavailable",
|
"statusUnhealthy": "Unavailable",
|
||||||
"statusUnknown": "Unknown",
|
"statusUnknown": "Unknown",
|
||||||
"responseTime": "Response Time",
|
"responseTime": "Response Time",
|
||||||
|
"enabled": "Enabled",
|
||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
"check": "Check",
|
"check": "Check",
|
||||||
|
"enableSuccess": "Enabled successfully",
|
||||||
|
"disableSuccess": "Disabled successfully",
|
||||||
|
"updateFailed": "Update failed",
|
||||||
"checkSuccess": "Check completed",
|
"checkSuccess": "Check completed",
|
||||||
"checkFailed": "Check failed",
|
"checkFailed": "Check failed",
|
||||||
"deleteConfirm": "Are you sure you want to delete this node?",
|
"deleteConfirm": "Are you sure you want to delete this node?",
|
||||||
|
|||||||
@@ -1084,8 +1084,12 @@
|
|||||||
"statusUnhealthy": "不可用",
|
"statusUnhealthy": "不可用",
|
||||||
"statusUnknown": "未知",
|
"statusUnknown": "未知",
|
||||||
"responseTime": "响应时间",
|
"responseTime": "响应时间",
|
||||||
|
"enabled": "启用",
|
||||||
"actions": "操作",
|
"actions": "操作",
|
||||||
"check": "检查",
|
"check": "检查",
|
||||||
|
"enableSuccess": "启用成功",
|
||||||
|
"disableSuccess": "禁用成功",
|
||||||
|
"updateFailed": "更新失败",
|
||||||
"checkSuccess": "检查完成",
|
"checkSuccess": "检查完成",
|
||||||
"checkFailed": "检查失败",
|
"checkFailed": "检查失败",
|
||||||
"deleteConfirm": "确定删除此节点吗?",
|
"deleteConfirm": "确定删除此节点吗?",
|
||||||
|
|||||||
@@ -1084,8 +1084,12 @@
|
|||||||
"statusUnhealthy": "不可用",
|
"statusUnhealthy": "不可用",
|
||||||
"statusUnknown": "未知",
|
"statusUnknown": "未知",
|
||||||
"responseTime": "響應時間",
|
"responseTime": "響應時間",
|
||||||
|
"enabled": "啟用",
|
||||||
"actions": "操作",
|
"actions": "操作",
|
||||||
"check": "檢查",
|
"check": "檢查",
|
||||||
|
"enableSuccess": "啟用成功",
|
||||||
|
"disableSuccess": "禁用成功",
|
||||||
|
"updateFailed": "更新失敗",
|
||||||
"checkSuccess": "檢查完成",
|
"checkSuccess": "檢查完成",
|
||||||
"checkFailed": "檢查失敗",
|
"checkFailed": "檢查失敗",
|
||||||
"deleteConfirm": "確定刪除此節點嗎?",
|
"deleteConfirm": "確定刪除此節點嗎?",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { Card, Table, Button, Space, Badge, message, Popconfirm, Tag } from 'antd'
|
import { Card, Table, Button, Space, Badge, message, Popconfirm, Tag, Switch } from 'antd'
|
||||||
import { UpOutlined, DownOutlined, DeleteOutlined, ReloadOutlined, PlusOutlined, ApiOutlined } from '@ant-design/icons'
|
import { UpOutlined, DownOutlined, DeleteOutlined, ReloadOutlined, PlusOutlined, ApiOutlined } from '@ant-design/icons'
|
||||||
import { apiService } from '../services/api'
|
import { apiService } from '../services/api'
|
||||||
import type { RpcNodeConfig } from '../types'
|
import type { RpcNodeConfig } from '../types'
|
||||||
@@ -92,6 +92,20 @@ const RpcNodeSettings: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleToggleEnabled = async (id: number, enabled: boolean) => {
|
||||||
|
try {
|
||||||
|
const response = await apiService.rpcNodes.update({ id, enabled })
|
||||||
|
if (response.data.code === 0) {
|
||||||
|
message.success(enabled ? t('rpcNodeSettings.enableSuccess') : t('rpcNodeSettings.disableSuccess'))
|
||||||
|
fetchNodes()
|
||||||
|
} else {
|
||||||
|
message.error(response.data.msg || t('rpcNodeSettings.updateFailed'))
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message || t('rpcNodeSettings.updateFailed'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: t('rpcNodeSettings.priority'),
|
title: t('rpcNodeSettings.priority'),
|
||||||
@@ -126,6 +140,17 @@ const RpcNodeSettings: React.FC = () => {
|
|||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
ellipsis: true
|
ellipsis: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('rpcNodeSettings.enabled'),
|
||||||
|
dataIndex: 'enabled',
|
||||||
|
width: 100,
|
||||||
|
render: (enabled: boolean, record: RpcNodeConfig) => (
|
||||||
|
<Switch
|
||||||
|
checked={enabled}
|
||||||
|
onChange={(checked) => handleToggleEnabled(record.id, checked)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('rpcNodeSettings.status'),
|
title: t('rpcNodeSettings.status'),
|
||||||
dataIndex: 'lastCheckStatus',
|
dataIndex: 'lastCheckStatus',
|
||||||
|
|||||||
Reference in New Issue
Block a user