diff --git a/pkg/router/router.go b/pkg/router/router.go index 3806da8..0e37181 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -7,6 +7,7 @@ import ( "fonchain-fiee/pkg/service/account" "fonchain-fiee/pkg/service/asChat" "fonchain-fiee/pkg/service/auth" + "fonchain-fiee/pkg/service/bundle" emailAlert "fonchain-fiee/pkg/service/emailAlerts" "fonchain-fiee/pkg/service/file" "fonchain-fiee/pkg/service/governance" @@ -280,6 +281,10 @@ func NewRouter() *gin.Engine { importRoute.GET("generate/photo/test2", imports.Test2) } + { + //健康检测 + v1.GET("health", bundle.HealthCheck) + } //静态文件 r.StaticFS("/api/fiee/static", http.Dir("./runtime")) r.NoRoute(func(c *gin.Context) { diff --git a/pkg/service/bundle/healthCheck.go b/pkg/service/bundle/healthCheck.go new file mode 100644 index 0000000..cdbfa6c --- /dev/null +++ b/pkg/service/bundle/healthCheck.go @@ -0,0 +1,181 @@ +package bundle + +import ( + "context" + "fonchain-fiee/api/bundle" + "fonchain-fiee/api/cast" + "fonchain-fiee/api/files" + "fonchain-fiee/api/governance" + "fonchain-fiee/api/order" + "fonchain-fiee/api/payment" + "fonchain-fiee/pkg/cache" + "fonchain-fiee/pkg/service" + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +// HealthCheck 健康检测接口 +func HealthCheck(c *gin.Context) { + healthStatus := gin.H{ + "status": "ok", + "timestamp": time.Now().Unix(), + "checks": make(map[string]interface{}), + } + + // 检查 Redis 连接 + redisStatus := "ok" + redisErr := "" + if cache.RedisClient != nil { + _, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + _, err := cache.RedisClient.Ping().Result() + if err != nil { + redisStatus = "failed" + redisErr = err.Error() + } + } else { + redisStatus = "failed" + redisErr = "Redis client is nil" + } + healthStatus["checks"].(map[string]interface{})["redis"] = gin.H{ + "status": redisStatus, + "error": redisErr, + } + + // 检查微服务连接(调用一个简单的服务方法) + serviceStatus := "ok" + serviceErr := "" + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + // 调用 BundleProvider 的简单方法进行健康检测 + _, err := service.BundleProvider.GetBundleBalanceByUserId(ctx, &bundle.GetBundleBalanceByUserIdReq{ + UserId: 0, // 使用一个不存在的用户ID,只检测服务连通性 + }) + if err != nil { + // 检查是否是超时或连接错误 + if ctx.Err() == context.DeadlineExceeded { + serviceStatus = "Bundle Service timeout" + serviceErr = "Service timeout" + } else { + // 其他错误(如业务错误)认为服务是可用的 + serviceStatus = "ok" + serviceErr = "" + } + } + healthStatus["checks"].(map[string]interface{})["bundleService"] = gin.H{ + "status": serviceStatus, + "error": serviceErr, + } + //调用 OrderProvider 的简单方法进行健康检测 + _, err = service.OrderProvider.GetOrder(ctx, &order.CommonRequest{ + ID: 0, + Domain: "", + SeriesUid: "", // 使用一个不存在的订单ID,只检测服务连通性 + }) + if err != nil { + if ctx.Err() == context.DeadlineExceeded { + serviceStatus = "Order Service timeout" + serviceErr = "Service timeout" + } else { + // 其他错误(如业务错误)认为服务是可用的 + serviceStatus = "ok" + serviceErr = "" + } + } + healthStatus["checks"].(map[string]interface{})["orderService"] = gin.H{ + "status": serviceStatus, + "error": serviceErr, + } + //调用 FilesProvider 的简单方法进行健康检测 + _, err = service.FilesProvider.List(ctx, &files.FileListReq{ + Path: "/", + UserSpacePath: "/fiee", + Page: 1, + PageSize: 50, + }) + if err != nil { + if ctx.Err() == context.DeadlineExceeded { + serviceStatus = "Filesbrowser Service timeout" + serviceErr = "Service timeout" + } else { + // 其他错误(如业务错误)认为服务是可用的 + serviceStatus = "ok" + serviceErr = "" + } + } + healthStatus["checks"].(map[string]interface{})["filesbrowserService"] = gin.H{ + "status": serviceStatus, + "error": serviceErr, + } + //调用 PaymentProvider 的简单方法进行健康检测 + _, err = service.PaymentProvider.QueryPayByOutTradeNo(ctx, &payment.PayQueryRequest{ + PayType: "2", + OutTradeNo: "1234567890", + }) + if err != nil { + if ctx.Err() == context.DeadlineExceeded { + serviceStatus = "Payment Service timeout" + serviceErr = "Service timeout" + } else { + // 其他错误(如业务错误)认为服务是可用的 + serviceStatus = "ok" + serviceErr = "" + } + } + healthStatus["checks"].(map[string]interface{})["paymentService"] = gin.H{ + "status": serviceStatus, + "error": serviceErr, + } + + //调用 CastProvider 的简单方法进行健康检测 + _, err = service.CastProvider.WorkList(ctx, &cast.WorkListReq{ + Page: 1, + PageSize: 10, + }) + if err != nil { + if ctx.Err() == context.DeadlineExceeded { + serviceStatus = "Cast Service timeout" + serviceErr = "Service timeout" + } else { + // 其他错误(如业务错误)认为服务是可用的 + serviceStatus = "ok" + serviceErr = "" + } + } + healthStatus["checks"].(map[string]interface{})["castService"] = gin.H{ + "status": serviceStatus, + "error": serviceErr, + } + //调用 GovernanceProvider 的简单方法进行健康检测 + _, err = service.GovernanceProvider.List(ctx, &governance.ListReq{ + Page: 1, + PageSize: 10, + }) + if err != nil { + if ctx.Err() == context.DeadlineExceeded { + serviceStatus = "Document Service timeout" + serviceErr = "Service timeout" + } else { + // 其他错误(如业务错误)认为服务是可用的 + serviceStatus = "ok" + serviceErr = "" + } + } + healthStatus["checks"].(map[string]interface{})["documentService"] = gin.H{ + "status": serviceStatus, + "error": serviceErr, + } + + // 如果所有检查都通过,返回 200,否则返回 503 + httpStatus := http.StatusOK + if redisStatus != "ok" || serviceStatus != "ok" { + httpStatus = http.StatusServiceUnavailable + healthStatus["status"] = "degraded" + } + + c.JSON(httpStatus, healthStatus) + service.Success(c, healthStatus) +}