This commit is contained in:
2026-04-13 23:18:15 +08:00
parent f030f9c5c9
commit e84cb75dda
3 changed files with 876 additions and 986 deletions

View File

@@ -1,334 +1,317 @@
/** /**
* 服务器硬件监控页专用:对接 DC-Hardware 服务(/DC-Hardware/v1 * 服务器硬件监控页专用:对接 DC-Hardware 服务(/DC-Hardware/v1
* 与「机房设备监控」等业务区分,勿混用命名。 * 与「机房设备监控」等业务区分,勿混用命名。
*/ */
import { request } from '@/api/request' import { request } from '@/api/request'
const HW_PREFIX = '/DC-Hardware/v1' const HW_PREFIX = '/DC-Hardware/v1'
/** 与 bsm-sdk 成功响应一致:业务数据在 details */ /** 与 bsm-sdk 成功响应一致:业务数据在 details */
export interface HostHardwareApiEnvelope<T = unknown> { export interface HostHardwareApiEnvelope<T = unknown> {
code?: number | string code?: number | string
message?: string message?: string
details?: T details?: T
} }
export interface HostHardwareDeviceDetailPayload { export interface HostHardwareDeviceDetailPayload {
device: HostHardwareDevice device: HostHardwareDevice
status: HostHardwareStatus | null status: HostHardwareStatus | null
} }
export interface HostHardwareDevice { export interface HostHardwareDevice {
id: string id: string
name?: string name?: string
ip: string ip: string
/** 设备类别,如 server/switch/storage未返回时前端可按 server 展示 */ /** 设备类别,如 server/switch/storage未返回时前端可按 server 展示 */
type?: string type?: string
protocol: string protocol: string
manufacturer?: string manufacturer?: string
model?: string model?: string
serial_number?: string serial_number?: string
username?: string username?: string
password?: string password?: string
port?: number port?: number
snmp_port?: number snmp_port?: number
community?: string community?: string
snmp_version?: string snmp_version?: string
redfish_base_url?: string redfish_base_url?: string
redfish_tls_skip_verify?: boolean redfish_tls_skip_verify?: boolean
ipmi_timeout_seconds?: number ipmi_timeout_seconds?: number
snmp_timeout_seconds?: number snmp_timeout_seconds?: number
redfish_timeout_seconds?: number redfish_timeout_seconds?: number
ipmi_collect_enabled?: boolean ipmi_collect_enabled?: boolean
snmp_collect_enabled?: boolean snmp_collect_enabled?: boolean
redfish_collect_enabled?: boolean redfish_collect_enabled?: boolean
collect_interval?: number collect_interval?: number
asset_id?: string asset_id?: string
server_identity?: string server_identity?: string
enabled?: boolean enabled?: boolean
status?: string status?: string
tags?: string tags?: string
description?: string description?: string
extra_config?: string extra_config?: string
} }
/** 创建/更新 DC-Hardware 设备(与 CreateDeviceRequest 一致;密码在更新时为空则不修改) */ /** 创建/更新 DC-Hardware 设备(与 CreateDeviceRequest 一致;密码在更新时为空则不修改) */
export interface HostHardwareDeviceUpsert { export interface HostHardwareDeviceUpsert {
name: string name: string
ip: string ip: string
/** 省略或空则服务端默认 server */ /** 省略或空则服务端默认 server */
type?: string type?: string
protocol: string protocol: string
username?: string username?: string
password?: string password?: string
port?: number port?: number
snmp_port?: number snmp_port?: number
community?: string community?: string
snmp_version?: string snmp_version?: string
redfish_base_url?: string redfish_base_url?: string
redfish_tls_skip_verify?: boolean redfish_tls_skip_verify?: boolean
location?: string location?: string
description?: string description?: string
tags?: string tags?: string
asset_id?: string asset_id?: string
server_identity?: string server_identity?: string
extra_config?: string extra_config?: string
ipmi_timeout_seconds?: number ipmi_timeout_seconds?: number
snmp_timeout_seconds?: number snmp_timeout_seconds?: number
redfish_timeout_seconds?: number redfish_timeout_seconds?: number
ipmi_collect_enabled?: boolean ipmi_collect_enabled?: boolean
snmp_collect_enabled?: boolean snmp_collect_enabled?: boolean
redfish_collect_enabled?: boolean redfish_collect_enabled?: boolean
collect_interval?: number collect_interval?: number
} /** 是否启用监控调度 */
enabled?: boolean
export interface HostHardwareDeviceListPayload { }
total: number
page: string | number export interface HostHardwareDeviceListPayload {
page_size: string | number total: number
data: HostHardwareDevice[] page: string | number
} page_size: string | number
data: HostHardwareDevice[]
export interface HostHardwareStatus { }
id?: string
device_id?: string export interface HostHardwareStatus {
status?: string id?: string
power_status?: string device_id?: string
cpu_status?: string status?: string
memory_status?: string power_status?: string
disk_status?: string cpu_status?: string
fan_status?: string memory_status?: string
temperature_status?: string disk_status?: string
network_status?: string fan_status?: string
psu_status?: string temperature_status?: string
raid_status?: string network_status?: string
last_check_time?: string psu_status?: string
error_message?: string raid_status?: string
raw_data?: string last_check_time?: string
} error_message?: string
raw_data?: string
export interface HostHardwareMetricsRow { }
id?: string
device_id?: string export interface HostHardwareMetricsRow {
metric_name: string id?: string
metric_type: string device_id?: string
metric_value: number metric_name: string
unit?: string metric_type: string
status?: string metric_value: number
threshold?: number unit?: string
location?: string status?: string
collection_time?: string threshold?: number
} location?: string
collection_time?: string
export interface HostHardwareLatestCollectionPayload { }
device_id: string
collected_at?: string | null export interface HostHardwareLatestCollectionPayload {
status: HostHardwareStatus | null device_id: string
metrics: HostHardwareMetricsRow[] collected_at?: string | null
timescaledb?: boolean status: HostHardwareStatus | null
message?: string metrics: HostHardwareMetricsRow[]
server_identity?: string timescaledb?: boolean
} message?: string
server_identity?: string
interface RawCollectionMetric { }
name?: string
type?: string interface RawCollectionMetric {
value?: number name?: string
unit?: string type?: string
status?: string value?: number
threshold?: number unit?: string
location?: string status?: string
} threshold?: number
location?: string
interface RawCollectionRoot { }
metrics?: RawCollectionMetric[]
} interface RawCollectionRoot {
metrics?: RawCollectionMetric[]
export interface NormalizedHostHardwareMetric { }
name: string
type: string export interface NormalizedHostHardwareMetric {
value: number name: string
unit: string type: string
status: string value: number
threshold?: number unit: string
location?: string status: string
} threshold?: number
location?: string
export interface HostHardwareMetricHistoryPayload { }
device_id: string
metric_name: string export interface HostHardwareMetricHistoryPayload {
start_time?: string device_id: string
end_time?: string metric_name: string
data: HostHardwareMetricsRow[] start_time?: string
timescaledb?: boolean end_time?: string
message?: string data: HostHardwareMetricsRow[]
} timescaledb?: boolean
message?: string
export interface HostHardwareStatisticsRow { }
device_id: string
stat_date: string export interface HostHardwareStatisticsRow {
online_time?: number device_id: string
offline_time?: number stat_date: string
warning_count?: number online_time?: number
critical_count?: number offline_time?: number
avg_temperature?: number warning_count?: number
max_temperature?: number critical_count?: number
min_temperature?: number avg_temperature?: number
avg_fan_speed?: number max_temperature?: number
avg_power_usage?: number min_temperature?: number
max_power_usage?: number avg_fan_speed?: number
total_check_count?: number avg_power_usage?: number
success_check_count?: number max_power_usage?: number
failed_check_count?: number total_check_count?: number
availability?: number success_check_count?: number
} failed_check_count?: number
availability?: number
export function isHostHardwareApiSuccess(res: HostHardwareApiEnvelope | null | undefined): boolean { }
const c = res?.code
if (c === 0 || c === '0') return true export function isHostHardwareApiSuccess(res: HostHardwareApiEnvelope | null | undefined): boolean {
// 少数网关/代理以 200 表示业务成功 const c = res?.code
if (c === 200 || c === '200') return true if (c === 0 || c === '0') return true
return false // 少数网关/代理以 200 表示业务成功
} if (c === 200 || c === '200') return true
return false
export function unwrapHostHardwareDetails<T>( }
res: (HostHardwareApiEnvelope<T> & { data?: T }) | null | undefined,
): T | null { export function unwrapHostHardwareDetails<T>(res: (HostHardwareApiEnvelope<T> & { data?: T }) | null | undefined): T | null {
if (!res || !isHostHardwareApiSuccess(res)) return null if (!res || !isHostHardwareApiSuccess(res)) return null
// 部分网关/SDK 将载荷放在 data 而非 details与 logs 等模块一致做兼容 // 部分网关/SDK 将载荷放在 data 而非 details与 logs 等模块一致做兼容
return res.details ?? res.data ?? null return res.details ?? res.data ?? null
} }
/** 按 server_identity 拉取最新一整轮采集JWT */ /** 按 server_identity 拉取最新一整轮采集JWT */
export function fetchHostHardwareLatestCollection(serverIdentity: string) { export function fetchHostHardwareLatestCollection(serverIdentity: string) {
return request.get<HostHardwareApiEnvelope<HostHardwareLatestCollectionPayload>>( return request.get<HostHardwareApiEnvelope<HostHardwareLatestCollectionPayload>>(
`${HW_PREFIX}/devices/by-server-identity/collection/latest`, `${HW_PREFIX}/devices/by-server-identity/collection/latest`,
{ params: { server_identity: serverIdentity } }, { params: { server_identity: serverIdentity } }
) )
} }
/** 设备详情(含最新一条 status */ /** 设备详情(含最新一条 status */
export function fetchHostHardwareDevice(deviceId: string) { export function fetchHostHardwareDevice(deviceId: string) {
return request.get<HostHardwareApiEnvelope<HostHardwareDeviceDetailPayload>>( return request.get<HostHardwareApiEnvelope<HostHardwareDeviceDetailPayload>>(`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}`)
`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}`, }
)
} /** 分页列表(可按 type / status / asset_id 筛选) */
export function fetchHostHardwareDeviceList(params?: {
/** 分页列表(可按 type / status / asset_id 筛选) */ type?: string
export function fetchHostHardwareDeviceList(params?: { status?: string
type?: string asset_id?: string
status?: string page?: number
asset_id?: string page_size?: number
page?: number }) {
page_size?: number return request.get<HostHardwareApiEnvelope<HostHardwareDeviceListPayload>>(`${HW_PREFIX}/devices`, {
}) { params,
return request.get<HostHardwareApiEnvelope<HostHardwareDeviceListPayload>>(`${HW_PREFIX}/devices`, { })
params, }
})
} /** 创建设备 */
export function createHostHardwareDevice(data: HostHardwareDeviceUpsert) {
/** 创建设备 */ return request.post<HostHardwareApiEnvelope<HostHardwareDevice>>(`${HW_PREFIX}/devices`, data)
export function createHostHardwareDevice(data: HostHardwareDeviceUpsert) { }
return request.post<HostHardwareApiEnvelope<HostHardwareDevice>>(`${HW_PREFIX}/devices`, data)
} /** 更新设备(全量必填字段;密码留空则不修改) */
export function updateHostHardwareDevice(deviceId: string, data: HostHardwareDeviceUpsert) {
/** 更新设备(全量必填字段;密码留空则不修改) */ return request.put<HostHardwareApiEnvelope<HostHardwareDevice>>(`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}`, data)
export function updateHostHardwareDevice(deviceId: string, data: HostHardwareDeviceUpsert) { }
return request.put<HostHardwareApiEnvelope<HostHardwareDevice>>(
`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}`, /** 异步立即采集 */
data, export function triggerHostHardwareCollect(deviceId: string) {
) return request.post<HostHardwareApiEnvelope<string>>(`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}/collect`)
} }
/** 异步立即采集 */ /** 启用监控 */
export function triggerHostHardwareCollect(deviceId: string) { export function enableHostHardwareDevice(deviceId: string) {
return request.post<HostHardwareApiEnvelope<string>>(`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}/collect`) return request.post<HostHardwareApiEnvelope<string>>(`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}/enable`)
} }
/** 用监控 */ /** 用监控 */
export function enableHostHardwareDevice(deviceId: string) { export function disableHostHardwareDevice(deviceId: string) {
return request.post<HostHardwareApiEnvelope<string>>(`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}/enable`) return request.post<HostHardwareApiEnvelope<string>>(`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}/disable`)
} }
/** 禁用监控 */ /** 单指标历史曲线JWT */
export function disableHostHardwareDevice(deviceId: string) { export function fetchHostHardwareMetricHistory(deviceId: string, metricName: string, startTime?: string, endTime?: string) {
return request.post<HostHardwareApiEnvelope<string>>(`${HW_PREFIX}/devices/${encodeURIComponent(deviceId)}/disable`) return request.get<HostHardwareApiEnvelope<HostHardwareMetricHistoryPayload>>(
} `${HW_PREFIX}/metrics/devices/${encodeURIComponent(deviceId)}/history`,
{
/** 单指标历史曲线JWT */ params: {
export function fetchHostHardwareMetricHistory( metric_name: metricName,
deviceId: string, ...(startTime ? { start_time: startTime } : {}),
metricName: string, ...(endTime ? { end_time: endTime } : {}),
startTime?: string, },
endTime?: string, }
) { )
return request.get<HostHardwareApiEnvelope<HostHardwareMetricHistoryPayload>>( }
`${HW_PREFIX}/metrics/devices/${encodeURIComponent(deviceId)}/history`,
{ /** 日汇总统计 */
params: { export function fetchHostHardwareStatistics(deviceId: string, startDate?: string, endDate?: string) {
metric_name: metricName, return request.get<HostHardwareApiEnvelope<HostHardwareStatisticsRow[]>>(`${HW_PREFIX}/metrics/statistics`, {
...(startTime ? { start_time: startTime } : {}), params: {
...(endTime ? { end_time: endTime } : {}), device_id: deviceId,
}, ...(startDate ? { start_date: startDate } : {}),
}, ...(endDate ? { end_date: endDate } : {}),
) },
} })
}
/** 日汇总统计 */
export function fetchHostHardwareStatistics( /**
deviceId: string, * 合并 API metrics 与 raw_data 兜底(无时序库时 metrics 可能为空)
startDate?: string, */
endDate?: string, export function normalizeHostHardwareMetrics(
) { status: HostHardwareStatus | null | undefined,
return request.get<HostHardwareApiEnvelope<HostHardwareStatisticsRow[]>>( metricsFromApi: HostHardwareMetricsRow[] | null | undefined
`${HW_PREFIX}/metrics/statistics`, ): NormalizedHostHardwareMetric[] {
{ const fromApi = metricsFromApi ?? []
params: { if (fromApi.length > 0) {
device_id: deviceId, return fromApi.map((m) => ({
...(startDate ? { start_date: startDate } : {}), name: m.metric_name,
...(endDate ? { end_date: endDate } : {}), type: m.metric_type,
}, value: m.metric_value,
}, unit: m.unit ?? '',
) status: m.status ?? 'ok',
} threshold: m.threshold,
location: m.location,
/** }))
* 合并 API metrics 与 raw_data 兜底(无时序库时 metrics 可能为空) }
*/ const raw = status?.raw_data
export function normalizeHostHardwareMetrics( if (!raw || typeof raw !== 'string') return []
status: HostHardwareStatus | null | undefined, try {
metricsFromApi: HostHardwareMetricsRow[] | null | undefined, const parsed = JSON.parse(raw) as RawCollectionRoot
): NormalizedHostHardwareMetric[] { const arr = parsed.metrics
const fromApi = metricsFromApi ?? [] if (!Array.isArray(arr)) return []
if (fromApi.length > 0) { return arr
return fromApi.map((m) => ({ .map((m) => ({
name: m.metric_name, name: String(m.name ?? ''),
type: m.metric_type, type: String(m.type ?? ''),
value: m.metric_value, value: typeof m.value === 'number' ? m.value : Number(m.value) || 0,
unit: m.unit ?? '', unit: String(m.unit ?? ''),
status: m.status ?? 'ok', status: String(m.status ?? 'ok'),
threshold: m.threshold, threshold: m.threshold,
location: m.location, location: m.location ? String(m.location) : undefined,
})) }))
} .filter((m) => m.name || m.type)
const raw = status?.raw_data } catch {
if (!raw || typeof raw !== 'string') return [] return []
try { }
const parsed = JSON.parse(raw) as RawCollectionRoot }
const arr = parsed.metrics
if (!Array.isArray(arr)) return []
return arr
.map((m) => ({
name: String(m.name ?? ''),
type: String(m.type ?? ''),
value: typeof m.value === 'number' ? m.value : Number(m.value) || 0,
unit: String(m.unit ?? ''),
status: String(m.status ?? 'ok'),
threshold: m.threshold,
location: m.location ? String(m.location) : undefined,
}))
.filter((m) => m.name || m.type)
} catch {
return []
}
}

File diff suppressed because one or more lines are too long