333 lines
8.7 KiB
Go
333 lines
8.7 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"
|
|
|
|
apiEntities "tyapi-server/internal/domains/api/entities"
|
|
"tyapi-server/internal/infrastructure/database"
|
|
)
|
|
|
|
// 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 {
|
|
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{},
|
|
|
|
// api
|
|
&apiEntities.ApiUser{},
|
|
&apiEntities.ApiCall{},
|
|
)
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
}
|
|
|
|
// GetArticleService 获取文章服务 (用于 Worker)
|
|
func (app *Application) GetArticleService() interface{} {
|
|
// 这里需要从容器中获取文章服务
|
|
// 由于循环导入问题,暂时返回 nil
|
|
// 实际使用时需要通过其他方式获取
|
|
return nil
|
|
}
|