- 修复 health.go handler 直接操作响应的架构违规问题 - 为 model 字段添加 GORM comment 标签(account_role、base、role_permission) - 为 handler、service、store 包添加包级文档注释 - 清理 customer service 和 personal_customer handler 中注释掉的代码 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
121 lines
3.1 KiB
Go
121 lines
3.1 KiB
Go
package handler
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/redis/go-redis/v9"
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/break/junhong_cmp_fiber/pkg/response"
|
|
)
|
|
|
|
// HealthHandler 健康检查处理器
|
|
type HealthHandler struct {
|
|
db *gorm.DB
|
|
redis *redis.Client
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewHealthHandler 创建健康检查处理器实例
|
|
func NewHealthHandler(db *gorm.DB, redis *redis.Client, logger *zap.Logger) *HealthHandler {
|
|
return &HealthHandler{
|
|
db: db,
|
|
redis: redis,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// Check 健康检查
|
|
// GET /health
|
|
func (h *HealthHandler) Check(c *fiber.Ctx) error {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
|
|
healthStatus := fiber.Map{
|
|
"status": "healthy",
|
|
"timestamp": time.Now().Format(time.RFC3339),
|
|
"services": fiber.Map{},
|
|
}
|
|
|
|
services := healthStatus["services"].(fiber.Map)
|
|
allHealthy := true
|
|
|
|
// 检查 PostgreSQL
|
|
sqlDB, err := h.db.DB()
|
|
if err != nil {
|
|
h.logger.Error("获取 PostgreSQL DB 实例失败", zap.Error(err))
|
|
services["postgres"] = fiber.Map{
|
|
"status": "down",
|
|
"error": err.Error(),
|
|
}
|
|
allHealthy = false
|
|
} else {
|
|
if err := sqlDB.PingContext(ctx); err != nil {
|
|
h.logger.Error("PostgreSQL Ping 失败", zap.Error(err))
|
|
services["postgres"] = fiber.Map{
|
|
"status": "down",
|
|
"error": err.Error(),
|
|
}
|
|
allHealthy = false
|
|
} else {
|
|
// 获取连接池统计信息
|
|
stats := sqlDB.Stats()
|
|
services["postgres"] = fiber.Map{
|
|
"status": "up",
|
|
"open_conns": stats.OpenConnections,
|
|
"in_use": stats.InUse,
|
|
"idle": stats.Idle,
|
|
"wait_count": stats.WaitCount,
|
|
"wait_duration": stats.WaitDuration.String(),
|
|
"max_idle_close": stats.MaxIdleClosed,
|
|
"max_lifetime_close": stats.MaxLifetimeClosed,
|
|
}
|
|
}
|
|
}
|
|
|
|
// 检查 Redis
|
|
if err := h.redis.Ping(ctx).Err(); err != nil {
|
|
h.logger.Error("Redis Ping 失败", zap.Error(err))
|
|
services["redis"] = fiber.Map{
|
|
"status": "down",
|
|
"error": err.Error(),
|
|
}
|
|
allHealthy = false
|
|
} else {
|
|
// 获取 Redis 信息
|
|
poolStats := h.redis.PoolStats()
|
|
services["redis"] = fiber.Map{
|
|
"status": "up",
|
|
"hits": poolStats.Hits,
|
|
"misses": poolStats.Misses,
|
|
"timeouts": poolStats.Timeouts,
|
|
"total_conns": poolStats.TotalConns,
|
|
"idle_conns": poolStats.IdleConns,
|
|
"stale_conns": poolStats.StaleConns,
|
|
}
|
|
}
|
|
|
|
// 设置总体状态
|
|
if !allHealthy {
|
|
healthStatus["status"] = "degraded"
|
|
h.logger.Warn("健康检查失败: 部分服务不可用")
|
|
} else {
|
|
h.logger.Info("健康检查成功: 所有服务正常")
|
|
}
|
|
|
|
// 统一使用 response.Success 返回,状态信息在 data.status 中标记
|
|
// 健康检查端点本身能响应即视为成功,具体服务状态由 data.status 字段表示
|
|
return response.Success(c, healthStatus)
|
|
}
|
|
|
|
// HealthCheck 简单健康检查(保持向后兼容)
|
|
func HealthCheck(c *fiber.Ctx) error {
|
|
return response.Success(c, fiber.Map{
|
|
"status": "healthy",
|
|
"timestamp": time.Now().Format(time.RFC3339),
|
|
})
|
|
}
|