feat: 增加health健康检查和替换前端跳转路由api

This commit is contained in:
zerosaturation 2026-04-14 00:10:24 +08:00
parent 65ad41d1a4
commit a5e3008819
24 changed files with 409 additions and 304 deletions

View File

@ -1,6 +1,6 @@
# ==================== Server Configuration ====================
# Gin运行模式: debug, release, test
GIN_MODE=debug
GIN_MODE=release
# API网关端口
SERVER_PORT=8080

View File

@ -2,7 +2,7 @@
# 多机部署时将此文件放到 gateway 服务器的 /etc/topfans/gateway.env
# Gin 运行模式: debug, release
GIN_MODE=debug
GIN_MODE=release
# API 网关监听端口
SERVER_PORT=8080

View File

@ -0,0 +1,61 @@
package health
import (
"fmt"
"log"
"net/http"
"sync"
)
// Handler 健康检查处理器
type Handler struct {
serviceName string
port int
server *http.Server
wg sync.WaitGroup
}
// NewHandler 创建健康检查处理器
func NewHandler(serviceName string, port int) *Handler {
return &Handler{
serviceName: serviceName,
port: port,
}
}
// Start 启动健康检查 HTTP 服务器
func (h *Handler) Start() {
mux := http.NewServeMux()
mux.HandleFunc("/health", h.handleHealth)
h.server = &http.Server{
Addr: fmt.Sprintf(":%d", h.port),
Handler: mux,
}
h.wg.Add(1)
go func() {
defer h.wg.Done()
log.Printf("[%s] Health check server started on port %d", h.serviceName, h.port)
if err := h.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Printf("[%s] Health check server error: %v", h.serviceName, err)
}
}()
}
// Stop 停止健康检查 HTTP 服务器
func (h *Handler) Stop() {
if h.server != nil {
if err := h.server.Close(); err != nil {
log.Printf("[%s] Health check server close error: %v", h.serviceName, err)
}
}
h.wg.Wait()
log.Printf("[%s] Health check server stopped", h.serviceName)
}
func (h *Handler) handleHealth(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Header().Set("Content-Type", "application/json")
fmt.Fprintf(w, `{"status":"ok","service":"%s"}`, h.serviceName)
}

View File

@ -0,0 +1,65 @@
-- Migration: Add overall_end_time to activities table
-- Date: 2026-04-13
-- Description: Add overall_end_time column for activity overall end time management
-- Add overall_end_time column to activities if it doesn't exist
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'activities' AND column_name = 'overall_end_time'
) THEN
ALTER TABLE activities ADD COLUMN overall_end_time BIGINT DEFAULT 0;
RAISE NOTICE 'Column overall_end_time added to activities table';
ELSE
RAISE NOTICE 'Column overall_end_time already exists in activities table';
END IF;
END $$;
-- Add avatar_url column to fan_profiles if it doesn't exist
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'fan_profiles' AND column_name = 'avatar_url'
) THEN
ALTER TABLE fan_profiles ADD COLUMN avatar_url VARCHAR(500);
RAISE NOTICE 'Column avatar_url added to fan_profiles table';
ELSE
RAISE NOTICE 'Column avatar_url already exists in fan_profiles table';
END IF;
END $$;
-- Add is_original column to assets if it doesn't exist
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'assets' AND column_name = 'is_original'
) THEN
ALTER TABLE assets ADD COLUMN is_original BOOLEAN DEFAULT false;
RAISE NOTICE 'Column is_original added to assets table';
ELSE
RAISE NOTICE 'Column is_original already exists in assets table';
END IF;
END $$;
-- Add theme column to activities if it doesn't exist
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'activities' AND column_name = 'theme'
) THEN
ALTER TABLE activities ADD COLUMN theme VARCHAR(100);
RAISE NOTICE 'Column theme added to activities table';
ELSE
RAISE NOTICE 'Column theme already exists in activities table';
END IF;
END $$;
-- Migration complete
DO $$
BEGIN
RAISE NOTICE 'Migration completed successfully';
END $$;

View File

@ -14,6 +14,7 @@ import (
"dubbo.apache.org/dubbo-go/v3/server"
"github.com/topfans/backend/pkg/database"
"github.com/topfans/backend/pkg/health"
"github.com/topfans/backend/pkg/logger"
"github.com/topfans/backend/pkg/models"
pb "github.com/topfans/backend/pkg/proto/activity"
@ -32,6 +33,7 @@ var (
dbPassword = flag.String("db-password", getEnv("DB_PASSWORD", ""), "Database password")
dbName = flag.String("db-name", getEnv("DB_NAME", "top-fans"), "Database name")
userServiceURL = flag.String("user-service-url", getEnv("USER_SERVICE_URL", "tri://localhost:20000"), "User service URL")
healthHandler *health.Handler
)
func getEnv(key, fallback string) string {
@ -174,6 +176,11 @@ func createUserRPCClient() (activityClient.UserRPCClient, error) {
// initDubboService 初始化 Dubbo 服务
func initDubboService(activityProvider *provider.ActivityProvider) error {
// 启动健康检查 HTTP 服务器
healthPort := *port + 1000 // e.g., 20004 -> 21004
healthHandler = health.NewHandler("activity-service", healthPort)
healthHandler.Start()
// 创建 Dubbo Server
srv, err := server.NewServer(
server.WithServerProtocol(
@ -210,6 +217,11 @@ func gracefulShutdown() {
logger.Sugar.Info("Shutting down Activity Service...")
// 关闭健康检查服务器
if healthHandler != nil {
healthHandler.Stop()
}
// 关闭数据库连接
if err := database.Close(); err != nil {
logger.Sugar.Errorf("Error closing database: %v", err)

View File

@ -15,6 +15,7 @@ import (
"github.com/joho/godotenv"
"github.com/topfans/backend/pkg/database"
"github.com/topfans/backend/pkg/health"
"github.com/topfans/backend/pkg/logger"
"github.com/topfans/backend/pkg/models"
pbAsset "github.com/topfans/backend/pkg/proto/asset"
@ -35,6 +36,7 @@ var (
dbPassword = flag.String("db-password", getEnv("DB_PASSWORD", ""), "Database password")
dbName = flag.String("db-name", getEnv("DB_NAME", "top-fans"), "Database name")
userServiceURL = flag.String("user-service-url", getEnv("USER_SERVICE_URL", "tri://localhost:20000"), "User service URL")
healthHandler *health.Handler
)
func getEnv(key, fallback string) string {
@ -92,6 +94,11 @@ func main() {
}
logger.Logger.Info("Database initialized successfully")
// 启动健康检查 HTTP 服务器
healthPort := *port + 1000 // e.g., 20003 -> 21003
healthHandler = health.NewHandler("asset-service", healthPort)
healthHandler.Start()
// 自动迁移数据库表
if err := autoMigrate(); err != nil {
logger.Logger.Fatal(fmt.Sprintf("Failed to migrate database: %v", err))
@ -166,6 +173,11 @@ func main() {
<-quit
logger.Logger.Info("Shutting down Asset Service...")
// 关闭健康检查服务器
if healthHandler != nil {
healthHandler.Stop()
}
}
// autoMigrate 自动迁移数据库表

View File

@ -14,6 +14,7 @@ import (
"dubbo.apache.org/dubbo-go/v3/server"
"github.com/topfans/backend/pkg/database"
"github.com/topfans/backend/pkg/health"
"github.com/topfans/backend/pkg/logger"
"github.com/topfans/backend/pkg/models"
pbAsset "github.com/topfans/backend/pkg/proto/asset"
@ -34,6 +35,7 @@ var (
dbName = flag.String("db-name", getEnv("DB_NAME", "top-fans"), "Database name")
assetServiceURL = flag.String("asset-service-url", getEnv("ASSET_SERVICE_URL", "tri://localhost:20003"), "Asset service URL")
userServiceURL = flag.String("user-service-url", getEnv("USER_SERVICE_URL", "tri://localhost:20000"), "User service URL")
healthHandler *health.Handler
)
func getEnv(key, fallback string) string {
@ -88,6 +90,11 @@ func main() {
}
logger.Logger.Info("Database initialized successfully")
// 启动健康检查 HTTP 服务器
healthPort := *port + 1000 // e.g., 20001 -> 21001
healthHandler = health.NewHandler("gallery-service", healthPort)
healthHandler.Start()
// 创建 Repository 层实例
db := database.GetDB()
// 自动迁移展馆相关表booth_slots / exhibitions
@ -174,6 +181,11 @@ func main() {
logger.Logger.Info("Shutting down Gallery Service...")
// 停止健康检查服务器
if healthHandler != nil {
healthHandler.Stop()
}
// 停止清理 Worker
cleanupWorker.Stop()
logger.Logger.Info("Cleanup worker stopped")

View File

@ -14,6 +14,7 @@ import (
"dubbo.apache.org/dubbo-go/v3/server"
"github.com/topfans/backend/pkg/database"
"github.com/topfans/backend/pkg/health"
"github.com/topfans/backend/pkg/logger"
"github.com/topfans/backend/pkg/models"
pb "github.com/topfans/backend/pkg/proto/social"
@ -33,6 +34,7 @@ var (
dbName = flag.String("db-name", getEnv("DB_NAME", "top-fans"), "Database name")
userServiceURL = flag.String("user-service-url", getEnv("USER_SERVICE_URL", "tri://localhost:20000"), "User service URL")
assetServiceURL = flag.String("asset-service-url", getEnv("ASSET_SERVICE_URL", "tri://localhost:20003"), "Asset service URL")
healthHandler *health.Handler
)
func getEnv(key, fallback string) string {
@ -137,6 +139,11 @@ func gracefulShutdown() {
logger.Sugar.Info("Shutting down server...")
// 关闭健康检查服务器
if healthHandler != nil {
healthHandler.Stop()
}
// 关闭数据库连接
if err := database.Close(); err != nil {
logger.Sugar.Errorf("Error closing database: %v", err)
@ -147,6 +154,11 @@ func gracefulShutdown() {
// initDubboService 初始化Dubbo-go服务
func initDubboService() error {
// 启动健康检查 HTTP 服务器
healthPort := *port + 1000 // e.g., 20002 -> 21002
healthHandler = health.NewHandler("social-service", healthPort)
healthHandler.Start()
db := database.GetDB()
if db == nil {
return fmt.Errorf("database is not initialized")

View File

@ -13,6 +13,7 @@ import (
"dubbo.apache.org/dubbo-go/v3/server"
"github.com/topfans/backend/pkg/database"
"github.com/topfans/backend/pkg/health"
"github.com/topfans/backend/pkg/logger"
"github.com/topfans/backend/pkg/models"
pb "github.com/topfans/backend/pkg/proto/user"
@ -28,6 +29,7 @@ var (
dbUser = flag.String("db-user", getEnv("DB_USER", "postgres"), "Database user")
dbPassword = flag.String("db-password", getEnv("DB_PASSWORD", ""), "Database password")
dbName = flag.String("db-name", getEnv("DB_NAME", "top-fans"), "Database name")
healthHandler *health.Handler
)
func getEnv(key, fallback string) string {
@ -133,6 +135,11 @@ func gracefulShutdown() {
logger.Sugar.Info("Shutting down server...")
// 关闭健康检查服务器
if healthHandler != nil {
healthHandler.Stop()
}
// 关闭数据库连接
if err := database.Close(); err != nil {
logger.Sugar.Errorf("Error closing database: %v", err)
@ -143,6 +150,11 @@ func gracefulShutdown() {
// initDubboService 初始化Dubbo-go服务
func initDubboService() error {
// 启动健康检查 HTTP 服务器
healthPort := *port + 1000 // e.g., 20000 -> 21000
healthHandler = health.NewHandler("user-service", healthPort)
healthHandler.Start()
db := database.GetDB()
if db == nil {
return fmt.Errorf("database is not initialized")

View File

@ -84,7 +84,7 @@ services:
expose:
- "20000"
healthcheck:
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:20000 || exit 1"]
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21000 || exit 1"]
<<: *healthcheck
deploy:
resources:
@ -128,7 +128,7 @@ services:
expose:
- "20003"
healthcheck:
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:20003 || exit 1"]
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21003 || exit 1"]
<<: *healthcheck
deploy:
resources:
@ -167,7 +167,7 @@ services:
expose:
- "20002"
healthcheck:
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:20002 || exit 1"]
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21002 || exit 1"]
<<: *healthcheck
deploy:
resources:
@ -188,7 +188,7 @@ services:
restart: always
environment:
<<: *common-env
PORT: 20004
PORT: 20001
DB_HOST: postgres
DB_PORT: 5432
DB_USER: postgres
@ -204,9 +204,9 @@ services:
networks:
- topfans-net
expose:
- "20004"
- "20001"
healthcheck:
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:20004 || exit 1"]
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21001 || exit 1"]
<<: *healthcheck
deploy:
resources:
@ -227,7 +227,7 @@ services:
restart: always
environment:
<<: *common-env
PORT: 20005
PORT: 20004
DB_HOST: postgres
DB_PORT: 5432
DB_USER: postgres
@ -240,9 +240,9 @@ services:
networks:
- topfans-net
expose:
- "20005"
- "20004"
healthcheck:
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:20005 || exit 1"]
test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:21004 || exit 1"]
<<: *healthcheck
deploy:
resources:
@ -271,8 +271,8 @@ services:
DUBBO_USER_SERVICE_URL: tri://userservice:20000
DUBBO_SOCIAL_SERVICE_URL: tri://socialservice:20002
DUBBO_ASSET_SERVICE_URL: tri://assetservice:20003
DUBBO_GALLERY_SERVICE_URL: tri://galleryservice:20004
DUBBO_ACTIVITY_SERVICE_URL: tri://activityservice:20005
DUBBO_GALLERY_SERVICE_URL: tri://galleryservice:20001
DUBBO_ACTIVITY_SERVICE_URL: tri://activityservice:20004
depends_on:
userservice:
condition: service_started

File diff suppressed because it is too large Load Diff

View File

@ -481,16 +481,13 @@ const handleBack = async () => {
content: '确定要返回吗?未保存的数据将会丢失',
success: async (res) => {
if (res.confirm) {
uni.redirectTo({
url: '/pages/castlove/mall'
});
resetForm();
uni.navigateBack()
}
}
});
} else {
uni.redirectTo({
url: '/pages/castlove/mall'
});
uni.navigateBack()
}
};
@ -569,7 +566,7 @@ const handleSkip = async () => {
// resetForm();
//
uni.redirectTo({
uni.navigateTo({
url: '/pages/castlove/success'
});
@ -594,7 +591,7 @@ const handleTabChange = (newTab) => {
];
if (newTab >= 0 && newTab < routes.length) {
uni.redirectTo({ url: routes[newTab] });
uni.navigateTo({ url: routes[newTab] });
}
};

View File

@ -506,17 +506,13 @@ const handleBack = async () => {
//
resetForm();
// 广
uni.redirectTo({
url: '/pages/square/square'
});
uni.navigateBack();
}
}
});
} else {
//
uni.redirectTo({
url: '/pages/square/square'
});
uni.navigateBack();
}
};
@ -531,7 +527,7 @@ const handleTabChange = (newTab) => {
];
if (newTab >= 0 && newTab < routes.length) {
uni.redirectTo({
uni.navigateTo({
url: routes[newTab]
});
}

View File

@ -26,7 +26,7 @@ const handleTabChange = (newTab) => {
];
if (newTab >= 0 && newTab < routes.length) {
uni.redirectTo({
uni.navigateTo({
url: routes[newTab]
});
}

View File

@ -363,9 +363,7 @@ const formatCount = (count) => {
//
const handleBack = () => {
uni.redirectTo({
url: '/pages/square/square'
});
uni.navigateBack();
};
</script>

View File

@ -246,7 +246,7 @@ const handleStarActivityClick = async () => {
});
// API
const response = await getActivityListApi(starId, 'active', 1, 10);
const response = await getActivityListApi(starId, 1, 10);
uni.hideLoading();

View File

@ -240,7 +240,7 @@ const handleSkip = async () => {
uni.setStorageSync('temp_nft_data', JSON.stringify(nftData));
//
uni.redirectTo({
uni.navigateTo({
url: '/pages/castlove/success'
});

View File

@ -129,7 +129,7 @@ const handleSuccess = () => {
//
setTimeout(() => {
uni.redirectTo({
uni.navigateTo({
url: '/pages/discover/generation-result'
});
}, 1500);

View File

@ -692,7 +692,7 @@ const selectAsset = async () => {
uni.setStorageSync('temp_nft_data', JSON.stringify(nftData));
//
uni.redirectTo({
uni.navigateTo({
url: '/pages/castlove/success'
});
} catch (error) {

View File

@ -28,7 +28,7 @@ const handleTabChange = (newTab) => {
];
if (newTab >= 0 && newTab < routes.length) {
uni.redirectTo({
uni.navigateTo({
url: routes[newTab]
});
}

View File

@ -28,7 +28,7 @@ const handleTabChange = (newTab) => {
];
if (newTab >= 0 && newTab < routes.length) {
uni.redirectTo({
uni.navigateTo({
url: routes[newTab]
});
}

View File

@ -28,7 +28,7 @@ const handleTabChange = (newTab) => {
];
if (newTab >= 0 && newTab < routes.length) {
uni.redirectTo({
uni.navigateTo({
url: routes[newTab]
});
}

View File

@ -156,7 +156,7 @@ function handleTabChange(newTab) {
]
if (newTab >= 0 && newTab < tabRoutes.length) {
uni.redirectTo({
uni.navigateTo({
url: tabRoutes[newTab],
fail: (err) => {
console.error('页面跳转失败:', err)

View File

@ -1,6 +1,6 @@
// API 基础配置
// const baseURL = 'http://101.132.250.62:8080'
const baseURL = 'http://192.168.110.60:8080'
const baseURL = 'http://101.132.250.62:8080'
// const baseURL = 'http://192.168.110.60:8080'
// const baseURL = 'http://localhost:8080'
// 是否使用模拟数据(开发调试时设为 true后端API准备好后改为 false