Files
opsxsql/frontend/src/features/datasource/utils.ts
T
Logan.Li ac498c7ba2 refactor(frontend): split 7 oversized pages into feature co-location
Per guide ch.7 page split pattern (skeleton orchestrates, private
components live in features/<domain>/):
- InstancePage 1716->328, UsersPage 1309->240, TicketsPage 1076->195,
  TicketDetailPage 911->287, QueriesPage 883->479, LdapConfigPage 860->393,
  WorkflowPage 852->194
- new feature modules: datasource/ rbac/ ticket/ query/ system/ldap/
  governance/workflow/ (components + hooks + types + utils)
- stable keys replace index keys in dynamic lists; polling intervals
  and toast call sites preserved 1:1 (verified by agents)
- filter/pagination state moved to URL query via new useQueryState hook
  (Instance/Users/Tickets/AuditLogs pages): refresh/share/back-button
  restore state; composite updates applied atomically
- permission pure functions extracted to lib/permissions.ts (used by
  useAuth + useLogin); page-size audit: 0 pages over 700-line budget
2026-10-26 17:54:15 +08:00

121 lines
4.6 KiB
TypeScript

/**
* 数据源域纯工具函数:状态/环境映射、列表筛选与健康分计算。
* 用途:供 InstancePage 及 features/datasource/components 下各组件共享,无副作用、不依赖 React。
* 已知限制:健康分为客户端按指标阈值估算的结果,并非后端权威评分。
*/
import type { InstanceItem } from './types';
/** 各数据库类型默认端口 */
export const DB_DEFAULT_PORTS: Record<string, number> = {
mysql: 3306, postgres: 5432, redis: 6379, mongo: 27017,
};
/** 环境展示名 */
export const envLabel: Record<string, string> = {
production: '生产', staging: '预发', development: '开发', test: '测试', testing: '测试', uat: 'UAT',
};
/** 环境徽标配色 */
export const envColor: Record<string, string> = {
production: 'bg-red-500/10 text-red-400 border-red-500/20',
staging: 'bg-yellow-500/10 text-yellow-400 border-yellow-500/20',
development: 'bg-green-500/10 text-green-400 border-green-500/20',
test: 'bg-blue-500/10 text-blue-400 border-blue-500/20',
testing: 'bg-blue-500/10 text-blue-400 border-blue-500/20',
uat: 'bg-cyan-500/10 text-cyan-400 border-cyan-500/20',
};
/** 状态展示信息(在线/离线/异常文案) */
const statusLabel: Record<string, { text: string; online: boolean }> = {
connected: { text: '在线', online: true },
online: { text: '在线', online: true },
active: { text: '在线', online: true },
disconnected: { text: '离线', online: false },
offline: { text: '离线', online: false },
error: { text: '异常', online: false },
};
/** 取状态展示信息,未知状态回退「未知」 */
export function getStatusInfo(status: string) {
return statusLabel[status] || { text: '未知', online: false };
}
/** 是否离线状态 */
export function isOfflineStatus(status: string): boolean {
return status === 'offline' || status === 'disconnected';
}
/** 状态筛选匹配(online/offline 归并为一类) */
export function matchesStatusFilter(status: string, filterStatus: string): boolean {
if (!filterStatus) return true;
const info = getStatusInfo(status);
if (filterStatus === 'online') return info.online;
if (filterStatus === 'offline') return isOfflineStatus(status);
return status === filterStatus;
}
/** 环境筛选匹配(test/testing 归并为一类) */
export function matchesEnvFilter(env: string, filterEnv: string): boolean {
if (!filterEnv) return true;
if (filterEnv === 'test') return env === 'test' || env === 'testing';
return env === filterEnv;
}
/** 计算实例健康分(0-100) */
export function calcHealthScore(inst: InstanceItem): number {
if (inst.status === 'offline' || inst.status === 'disconnected' || inst.status === 'error') return 0;
let score = 100;
const cpu = inst.cpu ?? 0;
const memory = inst.memory ?? 0;
const latency = inst.latency ?? 0;
const disk = inst.disk ?? 0;
if (cpu > 80) score -= 25;
else if (cpu > 60) score -= 10;
if (memory > 85) score -= 20;
else if (memory > 70) score -= 10;
if (latency > 10) score -= 20;
else if (latency > 5) score -= 10;
if (disk > 80) score -= 15;
return Math.max(0, score);
}
/** 健康分文字配色 */
export function healthColor(score: number): string {
if (score >= 90) return 'text-green-400';
if (score >= 70) return 'text-yellow-400';
if (score >= 50) return 'text-orange-400';
return 'text-red-400';
}
/** 健康分背景配色 */
export function healthBg(score: number): string {
if (score >= 90) return 'bg-green-500/15';
if (score >= 70) return 'bg-yellow-500/15';
if (score >= 50) return 'bg-orange-500/15';
return 'bg-red-500/15';
}
/** 健康分档位文案 */
export function healthLabel(score: number): string {
if (score >= 90) return '健康';
if (score >= 70) return '良好';
if (score >= 50) return '一般';
return '危险';
}
/** 列表筛选:按类型/状态/环境/关键字过滤,与页面原过滤逻辑一致 */
export function filterInstances(
instances: InstanceItem[],
criteria: { activeType: string; filterStatus: string; filterEnv: string; searchQuery: string },
): InstanceItem[] {
const { activeType, filterStatus, filterEnv, searchQuery } = criteria;
return instances.filter(i => {
if (activeType !== 'all' && (activeType === 'postgres' ? !['postgres', 'postgresql', 'pg'].includes(i.db_type) : i.db_type !== activeType)) return false;
if (!matchesStatusFilter(i.status, filterStatus)) return false;
if (!matchesEnvFilter(i.env, filterEnv)) return false;
if (searchQuery && !i.name.toLowerCase().includes(searchQuery.toLowerCase()) && !i.host.includes(searchQuery)) return false;
return true;
});
}