236 lines
5.8 KiB
Go
236 lines
5.8 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
|
|
"go.uber.org/zap"
|
|
"gorm.io/gorm"
|
|
|
|
"tyapi-server/internal/config"
|
|
"tyapi-server/internal/container"
|
|
"tyapi-server/internal/domains/user/entities"
|
|
)
|
|
|
|
// Application 应用程序结构
|
|
type Application struct {
|
|
container *container.Container
|
|
config *config.Config
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewApplication 创建新的应用程序实例
|
|
func NewApplication() (*Application, error) {
|
|
// 加载配置
|
|
cfg, err := config.LoadConfig()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to load config: %w", err)
|
|
}
|
|
|
|
// 创建日志器
|
|
logger, err := createLogger(cfg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create logger: %w", err)
|
|
}
|
|
|
|
// 创建容器
|
|
cont := container.NewContainer()
|
|
|
|
return &Application{
|
|
container: cont,
|
|
config: cfg,
|
|
logger: logger,
|
|
}, nil
|
|
}
|
|
|
|
// Run 运行应用程序
|
|
func (a *Application) Run() error {
|
|
// 打印启动信息
|
|
a.printBanner()
|
|
|
|
// 启动容器
|
|
a.logger.Info("Starting application container...")
|
|
if err := a.container.Start(); err != nil {
|
|
a.logger.Error("Failed to start container", zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
// 设置优雅关闭
|
|
a.setupGracefulShutdown()
|
|
|
|
a.logger.Info("Application started successfully",
|
|
zap.String("version", a.config.App.Version),
|
|
zap.String("environment", a.config.App.Env),
|
|
zap.String("port", a.config.Server.Port))
|
|
|
|
// 等待信号
|
|
return a.waitForShutdown()
|
|
}
|
|
|
|
// RunMigrations 运行数据库迁移
|
|
func (a *Application) RunMigrations() error {
|
|
a.logger.Info("Running database migrations...")
|
|
|
|
// 创建数据库连接
|
|
db, err := a.createDatabaseConnection()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create database connection: %w", err)
|
|
}
|
|
|
|
// 自动迁移
|
|
if err := a.autoMigrate(db); err != nil {
|
|
return fmt.Errorf("failed to run migrations: %w", err)
|
|
}
|
|
|
|
a.logger.Info("Database migrations completed successfully")
|
|
return nil
|
|
}
|
|
|
|
// printBanner 打印启动横幅
|
|
func (a *Application) printBanner() {
|
|
banner := fmt.Sprintf(`
|
|
╔══════════════════════════════════════════════════════════════╗
|
|
║ %s ║
|
|
║ Version: %s ║
|
|
║ Environment: %s ║
|
|
║ Port: %s ║
|
|
╚══════════════════════════════════════════════════════════════╝
|
|
`,
|
|
a.config.App.Name,
|
|
a.config.App.Version,
|
|
a.config.App.Env,
|
|
a.config.Server.Port,
|
|
)
|
|
|
|
fmt.Println(banner)
|
|
}
|
|
|
|
// setupGracefulShutdown 设置优雅关闭
|
|
func (a *Application) setupGracefulShutdown() {
|
|
c := make(chan os.Signal, 1)
|
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
<-c
|
|
a.logger.Info("Received shutdown signal, starting graceful shutdown...")
|
|
|
|
// 停止容器
|
|
if err := a.container.Stop(); err != nil {
|
|
a.logger.Error("Error during container shutdown", zap.Error(err))
|
|
}
|
|
|
|
a.logger.Info("Application shutdown completed")
|
|
os.Exit(0)
|
|
}()
|
|
}
|
|
|
|
// waitForShutdown 等待关闭信号
|
|
func (a *Application) waitForShutdown() error {
|
|
// 创建一个通道来等待关闭
|
|
done := make(chan bool, 1)
|
|
|
|
// 启动一个协程来监听信号
|
|
go func() {
|
|
c := make(chan os.Signal, 1)
|
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
|
<-c
|
|
done <- true
|
|
}()
|
|
|
|
// 等待关闭信号
|
|
<-done
|
|
return nil
|
|
}
|
|
|
|
// createDatabaseConnection 创建数据库连接
|
|
func (a *Application) createDatabaseConnection() (*gorm.DB, error) {
|
|
return container.NewDatabase(a.config, a.logger)
|
|
}
|
|
|
|
// autoMigrate 自动迁移
|
|
func (a *Application) autoMigrate(db *gorm.DB) error {
|
|
// 迁移用户相关表
|
|
return db.AutoMigrate(
|
|
&entities.User{},
|
|
// 后续可以添加其他实体
|
|
)
|
|
}
|
|
|
|
// createLogger 创建日志器
|
|
func createLogger(cfg *config.Config) (*zap.Logger, error) {
|
|
level, err := zap.ParseAtomicLevel(cfg.Logger.Level)
|
|
if err != nil {
|
|
level = zap.NewAtomicLevelAt(zap.InfoLevel)
|
|
}
|
|
|
|
config := zap.Config{
|
|
Level: level,
|
|
Development: cfg.App.IsDevelopment(),
|
|
Encoding: cfg.Logger.Format,
|
|
EncoderConfig: zap.NewProductionEncoderConfig(),
|
|
OutputPaths: []string{cfg.Logger.Output},
|
|
ErrorOutputPaths: []string{"stderr"},
|
|
}
|
|
|
|
if cfg.Logger.Format == "" {
|
|
config.Encoding = "json"
|
|
}
|
|
if cfg.Logger.Output == "" {
|
|
config.OutputPaths = []string{"stdout"}
|
|
}
|
|
|
|
return config.Build()
|
|
}
|
|
|
|
// GetConfig 获取配置
|
|
func (a *Application) GetConfig() *config.Config {
|
|
return a.config
|
|
}
|
|
|
|
// GetLogger 获取日志器
|
|
func (a *Application) GetLogger() *zap.Logger {
|
|
return a.logger
|
|
}
|
|
|
|
// HealthCheck 应用程序健康检查
|
|
func (a *Application) HealthCheck() error {
|
|
// 这里可以添加应用程序级别的健康检查逻辑
|
|
return nil
|
|
}
|
|
|
|
// GetVersion 获取版本信息
|
|
func (a *Application) GetVersion() map[string]string {
|
|
return map[string]string{
|
|
"name": a.config.App.Name,
|
|
"version": a.config.App.Version,
|
|
"environment": a.config.App.Env,
|
|
"go_version": "1.23.4+",
|
|
}
|
|
}
|
|
|
|
// RunCommand 运行特定命令
|
|
func (a *Application) RunCommand(command string, args ...string) error {
|
|
switch command {
|
|
case "migrate":
|
|
return a.RunMigrations()
|
|
case "version":
|
|
version := a.GetVersion()
|
|
fmt.Printf("Name: %s\n", version["name"])
|
|
fmt.Printf("Version: %s\n", version["version"])
|
|
fmt.Printf("Environment: %s\n", version["environment"])
|
|
fmt.Printf("Go Version: %s\n", version["go_version"])
|
|
return nil
|
|
case "health":
|
|
if err := a.HealthCheck(); err != nil {
|
|
fmt.Printf("Health check failed: %v\n", err)
|
|
return err
|
|
}
|
|
fmt.Println("Application is healthy")
|
|
return nil
|
|
default:
|
|
return fmt.Errorf("unknown command: %s", command)
|
|
}
|
|
}
|