feat: 添加视频服务器双机热备状态
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
export interface HighAvailable {
|
||||||
|
pyip: string;
|
||||||
|
vip: string;
|
||||||
|
changeDate: string;
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export * from './diag';
|
export * from './diag';
|
||||||
|
export * from './high-available';
|
||||||
export * from './link-description';
|
export * from './link-description';
|
||||||
export * from './station';
|
export * from './station';
|
||||||
|
|||||||
@@ -1,5 +1,26 @@
|
|||||||
import { ndmClient, userClient, type MediaServerStatus, type SendRtpInfo, type Station } from '@/apis';
|
import { ndmClient, userClient, type HighAvailable, type MediaServerStatus, type SendRtpInfo, type Station } from '@/apis';
|
||||||
import { unwrapResponse } from '@/utils';
|
import { unwrapNullableResponse, unwrapResponse } from '@/utils';
|
||||||
|
import destr from 'destr';
|
||||||
|
|
||||||
|
// {
|
||||||
|
// "code": 0,
|
||||||
|
// "data": "{pyip:\"10.18.128.14\",vip:\"10.18.128.6\",changeDate:\"2026-03-23 15:55:00\"}",
|
||||||
|
// "msg": "ok",
|
||||||
|
// "path": null,
|
||||||
|
// "extra": null,
|
||||||
|
// "timestamp": "1774421387908",
|
||||||
|
// "errorMsg": "",
|
||||||
|
// "isSuccess": true
|
||||||
|
// }
|
||||||
|
export const getHighAvailableApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
|
const { stationCode, signal } = options ?? {};
|
||||||
|
const client = stationCode ? ndmClient : userClient;
|
||||||
|
const prefix = stationCode ? `/${stationCode}` : '';
|
||||||
|
const endpoint = `${prefix}/api/ndm/ndmServiceAvailable/highAvailable/get`;
|
||||||
|
const resp = await client.get<string | null>(endpoint, { signal });
|
||||||
|
const data = unwrapNullableResponse(resp);
|
||||||
|
return destr<HighAvailable | null>(data);
|
||||||
|
};
|
||||||
|
|
||||||
export const getAllPushApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
export const getAllPushApi = async (options?: { stationCode?: Station['code']; signal?: AbortSignal }) => {
|
||||||
const { stationCode, signal } = options ?? {};
|
const { stationCode, signal } = options ?? {};
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import ServerAlive from './server-alive.vue';
|
import ServerAlive from './server-alive.vue';
|
||||||
import ServerCard from './server-card.vue';
|
import ServerCard from './server-card.vue';
|
||||||
import ServerCurrentDiag from './server-current-diag.vue';
|
import ServerCurrentDiag from './server-current-diag.vue';
|
||||||
|
import ServerHighAvailable from './server-high-available.vue';
|
||||||
import ServerHistoryDiag from './server-history-diag.vue';
|
import ServerHistoryDiag from './server-history-diag.vue';
|
||||||
import ServerStreamPush from './server-stream-push.vue';
|
import ServerStreamPush from './server-stream-push.vue';
|
||||||
import ServerUpdate from './server-update.vue';
|
import ServerUpdate from './server-update.vue';
|
||||||
|
|
||||||
export { ServerAlive, ServerCard, ServerCurrentDiag, ServerHistoryDiag, ServerUpdate, ServerStreamPush };
|
export { ServerAlive, ServerCard, ServerCurrentDiag, ServerHighAvailable, ServerHistoryDiag, ServerUpdate, ServerStreamPush };
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type NdmServerDiagInfo, type NdmServerResultVO, type Station } from '@/apis';
|
import { type NdmServerDiagInfo, type NdmServerResultVO, type Station } from '@/apis';
|
||||||
import { DeviceHardwareCard, DeviceHeaderCard, ServerAlive, ServerStreamPush } from '@/components';
|
import { DeviceHardwareCard, DeviceHeaderCard, ServerAlive, ServerHighAvailable, ServerStreamPush } from '@/components';
|
||||||
import destr from 'destr';
|
import destr from 'destr';
|
||||||
import { NFlex } from 'naive-ui';
|
import { NFlex } from 'naive-ui';
|
||||||
import { computed, toRefs } from 'vue';
|
import { computed, toRefs } from 'vue';
|
||||||
@@ -27,6 +27,7 @@ const runningTime = computed(() => lastDiagInfo.value?.commInfo?.系统运行时
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NFlex vertical>
|
<NFlex vertical>
|
||||||
|
<ServerHighAvailable :ndm-device="ndmDevice" :station="station" />
|
||||||
<DeviceHeaderCard :ndm-device="ndmDevice" :station="station" />
|
<DeviceHeaderCard :ndm-device="ndmDevice" :station="station" />
|
||||||
<DeviceHardwareCard running-time-label="服务器运行时间" :cpu-usage="cpuUsage" :mem-usage="memUsage" :disk-usage="diskUsage" :running-time="runningTime" />
|
<DeviceHardwareCard running-time-label="服务器运行时间" :cpu-usage="cpuUsage" :mem-usage="memUsage" :disk-usage="diskUsage" :running-time="runningTime" />
|
||||||
<ServerAlive :ndm-device="ndmDevice" :station="station" />
|
<ServerAlive :ndm-device="ndmDevice" :station="station" />
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { getHighAvailableApi, type NdmServerResultVO, type Station } from '@/apis';
|
||||||
|
import { DEVICE_TYPE_LITERALS, tryGetDeviceType } from '@/enums';
|
||||||
|
import { useSettingStore } from '@/stores';
|
||||||
|
import { useQuery, useQueryClient } from '@tanstack/vue-query';
|
||||||
|
import { NAlert, NCard, NFlex } from 'naive-ui';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
import { computed, toRefs, watch } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
ndmDevice: NdmServerResultVO;
|
||||||
|
station: Station;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const settingStore = useSettingStore();
|
||||||
|
const { activeRequests } = storeToRefs(settingStore);
|
||||||
|
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
const { ndmDevice, station } = toRefs(props);
|
||||||
|
|
||||||
|
const deviceType = computed(() => tryGetDeviceType(ndmDevice.value.deviceType));
|
||||||
|
|
||||||
|
const isVideoServer = computed(() => deviceType.value === DEVICE_TYPE_LITERALS.ndmVideoServer);
|
||||||
|
|
||||||
|
const SERVER_HIGH_AVAILABLE_QUERY_KEY = 'server-high-available-query';
|
||||||
|
|
||||||
|
const deviceUniqueKey = computed(() => [station.value.code, ndmDevice.value.id]);
|
||||||
|
|
||||||
|
const { data: highAvailable } = useQuery({
|
||||||
|
queryKey: computed(() => [SERVER_HIGH_AVAILABLE_QUERY_KEY, deviceUniqueKey.value, ndmDevice.value.lastDiagTime]),
|
||||||
|
enabled: computed(() => activeRequests.value && isVideoServer.value),
|
||||||
|
refetchInterval: 30 * 1000,
|
||||||
|
gcTime: 0,
|
||||||
|
queryFn: async ({ signal }) => {
|
||||||
|
const highAvailable = await getHighAvailableApi({ stationCode: station.value.code, signal });
|
||||||
|
return highAvailable;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
watch(activeRequests, (active) => {
|
||||||
|
if (!active) {
|
||||||
|
queryClient.cancelQueries({ queryKey: [SERVER_HIGH_AVAILABLE_QUERY_KEY] });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const showCard = computed(() => {
|
||||||
|
const { pyip: physicalIp } = highAvailable.value ?? {};
|
||||||
|
const ipAddressMatched = physicalIp === ndmDevice.value.ipAddress;
|
||||||
|
return isVideoServer.value && ipAddressMatched;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NAlert v-if="showCard && !!highAvailable" :bordered="false" type="success">
|
||||||
|
<template #header>
|
||||||
|
<NFlex :align="'center'">
|
||||||
|
<div>正在提供服务</div>
|
||||||
|
<NFlex :align="'center'" style="font-size: smaller">
|
||||||
|
<div>虚拟IP: {{ highAvailable.vip }}</div>
|
||||||
|
<div>启用时间: {{ highAvailable.changeDate }}</div>
|
||||||
|
</NFlex>
|
||||||
|
</NFlex>
|
||||||
|
</template>
|
||||||
|
</NAlert>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -136,6 +136,17 @@ export const unwrapResponse = <T>(resp: HttpResponse<T>) => {
|
|||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const unwrapNullableResponse = <T>(resp: HttpResponse<T>) => {
|
||||||
|
const [err, data, result] = resp;
|
||||||
|
if (err) throw err;
|
||||||
|
if (result) {
|
||||||
|
const { isSuccess, path, msg, errorMsg } = result;
|
||||||
|
if (!isSuccess) throw new Error(`${path ? `${path}: ` : ''}${msg || errorMsg || '请求失败'}`);
|
||||||
|
}
|
||||||
|
if (data === undefined) throw new Error('响应数据不存在');
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
// 针对没有数据的响应,直接判断是否存在错误
|
// 针对没有数据的响应,直接判断是否存在错误
|
||||||
export const unwrapVoidResponse = (resp: HttpResponse<void>) => {
|
export const unwrapVoidResponse = (resp: HttpResponse<void>) => {
|
||||||
const [err, , result] = resp;
|
const [err, , result] = resp;
|
||||||
|
|||||||
Reference in New Issue
Block a user