- 重构项目结构:controllers/services -> modules模块化组织 - 新增Drizzle ORM集成和数据库schema定义 - 添加完整的开发规范文档(.cursor/rules/) - 重新组织插件结构为子目录方式 - 新增用户模块和示例代码 - 更新类型定义并移除试验性代码 - 添加API文档和JWT使用示例 关联任务计划文档
300 lines
9.4 KiB
TypeScript
300 lines
9.4 KiB
TypeScript
/**
|
||
* @file 健康检查服务
|
||
* @author hotok
|
||
* @date 2025-06-28
|
||
* @lastEditor hotok
|
||
* @lastEditTime 2025-06-28
|
||
* @description 系统健康状态检查业务逻辑,包括数据库、Redis等依赖检查
|
||
*/
|
||
|
||
import type { Context } from 'elysia';
|
||
import { Redis } from '@/utils/redis';
|
||
import { pool } from '@/utils/mysql';
|
||
import { Logger } from '@/plugins/logger/logger.service';
|
||
|
||
// 临时内联类型定义
|
||
interface ComponentStatus {
|
||
status: 'healthy' | 'unhealthy' | 'degraded';
|
||
responseTime?: number;
|
||
error?: string;
|
||
details?: Record<string, any>;
|
||
}
|
||
|
||
interface HealthStatus {
|
||
code: number;
|
||
message: string;
|
||
data: {
|
||
status: 'healthy' | 'unhealthy' | 'degraded';
|
||
timestamp: string;
|
||
uptime: number;
|
||
responseTime: number;
|
||
version: string;
|
||
environment: string;
|
||
error?: string;
|
||
components: {
|
||
mysql?: ComponentStatus;
|
||
redis?: ComponentStatus;
|
||
[key: string]: ComponentStatus | undefined;
|
||
};
|
||
};
|
||
}
|
||
|
||
interface DetailedHealthStatus extends HealthStatus {
|
||
data: HealthStatus['data'] & {
|
||
system?: {
|
||
platform: string;
|
||
arch: string;
|
||
nodeVersion: string;
|
||
runtime: string;
|
||
pid: number;
|
||
cwd: string;
|
||
};
|
||
performance?: {
|
||
cpuUsage: {
|
||
user: number;
|
||
system: number;
|
||
};
|
||
memoryUsage: {
|
||
rss: number;
|
||
heapTotal: number;
|
||
heapUsed: number;
|
||
external: number;
|
||
arrayBuffers: number;
|
||
};
|
||
uptime: number;
|
||
};
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 健康检查服务类
|
||
* 提供系统及依赖服务的健康状态检查
|
||
*/
|
||
class HealthService {
|
||
/**
|
||
* Redis实例
|
||
*/
|
||
private redis: Redis;
|
||
|
||
constructor() {
|
||
this.redis = new Redis();
|
||
}
|
||
|
||
/**
|
||
* 获取基本健康状态
|
||
* @param ctx Elysia上下文
|
||
* @returns 健康状态信息
|
||
*/
|
||
async getHealthStatus(ctx: Context): Promise<HealthStatus> {
|
||
const startTime = Date.now();
|
||
const timestamp = new Date().toISOString();
|
||
|
||
try {
|
||
// 并行检查所有依赖
|
||
const [mysqlStatus, redisStatus] = await Promise.allSettled([
|
||
this.checkMysqlHealth(),
|
||
this.checkRedisHealth(),
|
||
]);
|
||
|
||
/** 系统整体状态 */
|
||
const overallStatus = this.determineOverallStatus([
|
||
mysqlStatus.status === 'fulfilled' ? mysqlStatus.value : { status: 'unhealthy', error: 'Connection failed' },
|
||
redisStatus.status === 'fulfilled' ? redisStatus.value : { status: 'unhealthy', error: 'Connection failed' },
|
||
]);
|
||
|
||
const responseTime = Date.now() - startTime;
|
||
|
||
return {
|
||
code: overallStatus === 'healthy' ? 0 : 1,
|
||
message: overallStatus === 'healthy' ? '所有服务运行正常' : '部分服务异常',
|
||
data: {
|
||
status: overallStatus,
|
||
timestamp,
|
||
uptime: process.uptime(),
|
||
responseTime,
|
||
version: process.env.npm_package_version || '1.0.0',
|
||
environment: process.env.NODE_ENV || 'development',
|
||
components: {
|
||
mysql: mysqlStatus.status === 'fulfilled' ? mysqlStatus.value : { status: 'unhealthy', error: 'Connection failed' },
|
||
redis: redisStatus.status === 'fulfilled' ? redisStatus.value : { status: 'unhealthy', error: 'Connection failed' },
|
||
},
|
||
},
|
||
};
|
||
} catch (error) {
|
||
Logger.error(error as Error);
|
||
return {
|
||
code: 1,
|
||
message: '健康检查异常',
|
||
data: {
|
||
status: 'unhealthy',
|
||
timestamp,
|
||
uptime: process.uptime(),
|
||
responseTime: Date.now() - startTime,
|
||
version: process.env.npm_package_version || '1.0.0',
|
||
environment: process.env.NODE_ENV || 'development',
|
||
error: 'Health check failed',
|
||
components: {},
|
||
},
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取详细健康状态
|
||
* @param ctx Elysia上下文
|
||
* @returns 详细健康状态信息
|
||
*/
|
||
async getDetailedHealthStatus(ctx: Context): Promise<DetailedHealthStatus> {
|
||
const startTime = Date.now();
|
||
const timestamp = new Date().toISOString();
|
||
|
||
try {
|
||
// 获取基本健康状态
|
||
const basicHealth = await this.getHealthStatus(ctx);
|
||
|
||
// 获取系统资源信息
|
||
const systemInfo = this.getSystemInfo();
|
||
|
||
return {
|
||
...basicHealth,
|
||
data: {
|
||
...basicHealth.data,
|
||
system: systemInfo,
|
||
performance: {
|
||
cpuUsage: process.cpuUsage(),
|
||
memoryUsage: process.memoryUsage(),
|
||
uptime: process.uptime(),
|
||
},
|
||
},
|
||
};
|
||
} catch (error) {
|
||
Logger.error(error as Error);
|
||
return {
|
||
code: 1,
|
||
message: '详细健康检查异常',
|
||
data: {
|
||
status: 'unhealthy',
|
||
timestamp,
|
||
uptime: process.uptime(),
|
||
responseTime: Date.now() - startTime,
|
||
version: process.env.npm_package_version || '1.0.0',
|
||
environment: process.env.NODE_ENV || 'development',
|
||
error: 'Detailed health check failed',
|
||
components: {},
|
||
},
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查MySQL健康状态
|
||
* @returns MySQL组件状态
|
||
*/
|
||
private async checkMysqlHealth(): Promise<ComponentStatus> {
|
||
try {
|
||
const startTime = Date.now();
|
||
await pool.execute('SELECT 1');
|
||
const responseTime = Date.now() - startTime;
|
||
|
||
return {
|
||
status: 'healthy',
|
||
responseTime,
|
||
details: {
|
||
connection: 'active',
|
||
host: process.env.DB_HOST || 'localhost',
|
||
port: process.env.DB_PORT || '3306',
|
||
},
|
||
};
|
||
} catch (error) {
|
||
Logger.error(error as Error);
|
||
return {
|
||
status: 'unhealthy',
|
||
error: (error as Error).message,
|
||
details: {
|
||
connection: 'failed',
|
||
host: process.env.DB_HOST || 'localhost',
|
||
port: process.env.DB_PORT || '3306',
|
||
},
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查Redis健康状态
|
||
* @returns Redis组件状态
|
||
*/
|
||
private async checkRedisHealth(): Promise<ComponentStatus> {
|
||
try {
|
||
const startTime = Date.now();
|
||
const isHealthy = await this.redis.checkRedisHealth();
|
||
const responseTime = Date.now() - startTime;
|
||
|
||
if (isHealthy) {
|
||
const redisStatus = this.redis.getRedisStatus();
|
||
return {
|
||
status: 'healthy',
|
||
responseTime,
|
||
details: {
|
||
connection: 'active',
|
||
...redisStatus.config,
|
||
},
|
||
};
|
||
} else {
|
||
return {
|
||
status: 'unhealthy',
|
||
error: 'Redis ping failed',
|
||
details: {
|
||
connection: 'failed',
|
||
},
|
||
};
|
||
}
|
||
} catch (error) {
|
||
Logger.error(error as Error);
|
||
return {
|
||
status: 'unhealthy',
|
||
error: (error as Error).message,
|
||
details: {
|
||
connection: 'failed',
|
||
},
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 确定整体状态
|
||
* @param components 各组件状态
|
||
* @returns 整体状态
|
||
*/
|
||
private determineOverallStatus(components: ComponentStatus[]): 'healthy' | 'unhealthy' | 'degraded' {
|
||
const healthyCount = components.filter(c => c.status === 'healthy').length;
|
||
const totalCount = components.length;
|
||
|
||
if (healthyCount === totalCount) {
|
||
return 'healthy';
|
||
} else if (healthyCount === 0) {
|
||
return 'unhealthy';
|
||
} else {
|
||
return 'degraded';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取系统信息
|
||
* @returns 系统信息
|
||
*/
|
||
private getSystemInfo() {
|
||
return {
|
||
platform: process.platform,
|
||
arch: process.arch,
|
||
nodeVersion: process.version,
|
||
runtime: 'Bun',
|
||
pid: process.pid,
|
||
cwd: process.cwd(),
|
||
};
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 导出健康检查服务实例
|
||
*/
|
||
export const healthService = new HealthService();
|