You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

80 lines
3.5 KiB

// 第三方服务巡检(svc_health)的前端共享类型与展示辅助。
// 巡检页(pages/svcinspect.vue) 与仪表盘告警条(components/SvcAlertBanner.vue) 共用,
// 保证两处对「状态怎么叫、原因怎么写、区域怎么显示」的口径完全一致。
// 与后端 comm/svchealth.go 的 SvcHealth 字段一一对应。
export interface SvcHealth {
app_name: string
svc_id: string
region: number // 0=服务默认配置,>0=该区域的覆盖配置
name: string
provider: string
categories: string
enable: boolean
status: 'ok' | 'warn' | 'error' | 'skip'
config_issues: string[] | null
probe_kind: 'none' | 'reachable' | 'credential'
probe_ok: boolean
probe_msg: string
latency_ms: number
fail_streak: number
last_ok_at: number
checked_at: number
duration_ms: number
}
// 区域枚举(与 pb.Region、serviceconfig.vue 的 REGIONS 一致)。
export const REGION_LABEL: Record<number, string> = {
1: '中国', 2: '美国', 3: '东亚', 4: '东南亚', 5: '南亚', 6: '中东', 7: '欧洲',
8: '俄罗斯', 9: '非洲', 10: '南美', 11: '北美', 12: '大洋洲', 13: '巴西', 14: '印度',
}
// 服务类别(与后端 svcCatXxx、serviceconfig.vue 的 SCAT 一致)。
export const CAT_LABEL: Record<number, string> = {
1: 'STT', 2: 'TTS', 3: 'MT', 4: 'AST', 5: '文本LLM',
6: '存储', 7: 'STS', 8: '录音识别', 9: '多媒体LLM', 10: 'MCP',
}
export const STATUS_META: Record<string, { label: string; color: string; bg: string }> = {
error: { label: '不可用', color: '#be123c', bg: '#ffe4e6' },
warn: { label: '有隐患', color: '#b45309', bg: '#fef3c7' },
ok: { label: '正常', color: '#047857', bg: '#d1fae5' },
skip: { label: '已停用', color: '#64748b', bg: '#f1f5f9' },
}
// 探测方式的人话说明。核心是让「未探测」不被误读成「已验证通过」。
export const PROBE_META: Record<string, { label: string; hint: string }> = {
credential: { label: '凭据已验证', hint: '已用真实凭据调通服务商接口' },
reachable: { label: '仅连通性', hint: '地址可达,但未验证凭据是否有效' },
none: { label: '未探测', hint: '暂无该服务商的探针,可用性未经验证' },
}
// healthReason 一句话说清「为什么是这个状态」,供告警条与表格摘要列使用。
// 优先级:致命配置问题 > 探测失败原因 > 非致命隐患 > 探测结论。
export function healthReason(h: SvcHealth): string {
const issues = h.config_issues || []
const fatal = issues.filter((s) => s.startsWith('[致命] '))
if (fatal.length) return fatal.map((s) => s.replace('[致命] ', '')).join(';')
if (h.status === 'error') return h.probe_msg || '巡检失败'
if (issues.length) return issues.join(';')
if (h.probe_kind === 'none') return h.probe_msg || '未验证可用性'
return h.probe_msg || ''
}
// healthLastRunText 把巡检时间戳渲染成「刚刚 / N 分钟前 / N 小时前 / 日期」。
export function healthLastRunText(ms: number): string {
if (!ms) return '尚未执行'
const diff = Date.now() - ms
if (diff < 60_000) return '刚刚'
if (diff < 3_600_000) return `${Math.floor(diff / 60_000)} 分钟前`
if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)} 小时前`
return new Date(ms).toLocaleString('zh-CN', { hour12: false })
}
// parseCats 把逗号分隔的 categories 解析成类别数值数组。
export function parseCats(categories: string): number[] {
return (categories || '')
.split(',')
.map((s) => Number(s.trim()))
.filter((n) => !Number.isNaN(n) && n > 0)
}