Files
tyapi-server/internal/app/app.go
2025-09-20 19:05:07 +08:00

339 lines
8.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"
// 认证域实体
certEntities "tyapi-server/internal/domains/certification/entities"
// 财务域实体
financeEntities "tyapi-server/internal/domains/finance/entities"
// 产品域实体
productEntities "tyapi-server/internal/domains/product/entities"
// 文章域实体
articleEntities "tyapi-server/internal/domains/article/entities"
// 统计域实体
statisticsEntities "tyapi-server/internal/domains/statistics/entities"
apiEntities "tyapi-server/internal/domains/api/entities"
"tyapi-server/internal/infrastructure/database"
taskEntities "tyapi-server/internal/infrastructure/task/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()
// 检查是否需要自动迁移
if a.config.Database.AutoMigrate {
a.logger.Info("Auto migration is enabled, running database migrations...")
if err := a.RunMigrations(); err != nil {
a.logger.Error("Auto migration failed", zap.Error(err))
return fmt.Errorf("auto migration failed: %w", err)
}
a.logger.Info("Auto migration completed successfully")
}
// 启动容器
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.logger.Info("Container started successfully, setting up graceful shutdown...")
// 设置优雅关闭
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 {
return nil
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) {
dbCfg := database.Config{
Host: a.config.Database.Host,
Port: a.config.Database.Port,
User: a.config.Database.User,
Password: a.config.Database.Password,
Name: a.config.Database.Name,
SSLMode: a.config.Database.SSLMode,
Timezone: a.config.Database.Timezone,
MaxOpenConns: a.config.Database.MaxOpenConns,
MaxIdleConns: a.config.Database.MaxIdleConns,
ConnMaxLifetime: a.config.Database.ConnMaxLifetime,
}
db, err := database.NewConnection(dbCfg)
if err != nil {
return nil, err
}
return db.DB, nil
}
// autoMigrate 自动迁移
func (a *Application) autoMigrate(db *gorm.DB) error {
a.logger.Info("Starting database auto migration...")
// 如果需要删除某些表,可以在这里手动删除
// 注意:这会永久删除数据,请谨慎使用!
/*
// 删除不再需要的表(示例,请根据实际情况使用)
if err := db.Migrator().DropTable(&entities.FavoriteItem{}); err != nil {
a.logger.Warn("Failed to drop table", zap.Error(err))
// 继续执行,不阻断迁移
}
*/
// 自动迁移所有实体
return db.AutoMigrate(
// 用户域
&entities.User{},
&entities.SMSCode{},
&entities.EnterpriseInfo{},
&entities.ContractInfo{},
// 认证域
&certEntities.Certification{},
&certEntities.EnterpriseInfoSubmitRecord{},
&certEntities.EsignContractGenerateRecord{},
&certEntities.EsignContractSignRecord{},
// 财务域
&financeEntities.Wallet{},
&financeEntities.WalletTransaction{},
&financeEntities.RechargeRecord{},
&financeEntities.AlipayOrder{},
&financeEntities.InvoiceApplication{},
&financeEntities.UserInvoiceInfo{},
// 产品域
&productEntities.Product{},
&productEntities.ProductPackageItem{},
&productEntities.ProductCategory{},
&productEntities.Subscription{},
&productEntities.ProductDocumentation{},
&productEntities.ProductApiConfig{},
// 文章域
&articleEntities.Article{},
&articleEntities.Category{},
&articleEntities.Tag{},
&articleEntities.ScheduledTask{},
// 统计域
&statisticsEntities.StatisticsMetric{},
&statisticsEntities.StatisticsDashboard{},
&statisticsEntities.StatisticsReport{},
// api
&apiEntities.ApiUser{},
&apiEntities.ApiCall{},
// 任务域
&taskEntities.AsyncTask{},
)
}
// 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)
}
}