Files
pay-bridge/backend/internal/app/app.go
2026-03-13 15:51:59 +08:00

127 lines
3.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package app
import (
"context"
"log/slog"
"github.com/go-redis/redis/v8"
"gorm.io/gorm"
"pay-bridge/internal/repository"
"pay-bridge/internal/service"
"pay-bridge/pkg/config"
"pay-bridge/pkg/sequence"
)
// App 应用容器,持有所有初始化完成的 service 实例
type App struct {
Cfg *config.Config
// 对外暴露供 router 使用的 service
AdminAuthSvc *service.AdminAuthService
AppSvc *service.AppService
TradeSvc *service.TradeService
RefundSvc *service.RefundService
NotifySvc *service.NotifyService
MatchSvc *service.PaymentMatchService
MerchantSvc *service.MerchantService
ReconSvc *service.ReconciliationService
ChannelSvc *service.ChannelService
// 内部资源
db *gorm.DB
rdb *redis.Client
}
// New 初始化所有基础设施和 service返回就绪的 App 实例
func New(cfg *config.Config) (*App, error) {
a := &App{Cfg: cfg}
if err := a.initInfra(); err != nil {
return nil, err
}
a.initServices()
return a, nil
}
// Start 启动后台任务notify poller 等)
func (a *App) Start(ctx context.Context) {
a.NotifySvc.StartPoller(ctx, a.Cfg.Notify.PollerInterval, a.Cfg.Notify.PollerBatch)
}
// Shutdown 优雅关闭:关闭 DB 和 Redis 连接
func (a *App) Shutdown(ctx context.Context) {
if a.rdb != nil {
if err := a.rdb.Close(); err != nil {
slog.Error("redis close error", "err", err)
}
}
if a.db != nil {
sqlDB, err := a.db.DB()
if err == nil {
if err := sqlDB.Close(); err != nil {
slog.Error("db close error", "err", err)
}
}
}
}
// initInfra 初始化 DB 和 Redis
func (a *App) initInfra() error {
db, err := config.NewDB(a.Cfg.Database)
if err != nil {
return err
}
a.db = db
rdb, err := config.NewRedis(a.Cfg.Redis)
if err != nil {
return err
}
a.rdb = rdb
return nil
}
// initServices 按依赖顺序构建 repo → service
func (a *App) initServices() {
encKey := a.Cfg.Security.FieldEncryptKey
// Repositories
adminUserRepo := repository.NewAdminUserRepository(a.db)
appRepo := repository.NewAppRepository(a.db)
tradeRepo := repository.NewTradeOrderRepository(a.db)
refundRepo := repository.NewRefundOrderRepository(a.db)
notifyRepo := repository.NewNotifyLogRepository(a.db)
channelCfgRepo := repository.NewChannelConfigRepository(a.db)
seqRepo := repository.NewSequenceRepository(a.db)
profitSharingRepo := repository.NewProfitSharingRepository(a.db)
serviceFeeRepo := repository.NewServiceFeeRepository(a.db)
matchRepo := repository.NewPaymentMatchRepository(a.db)
merchantRepo := repository.NewMerchantRepository(a.db)
wechatRepo := repository.NewWechatRepository(a.db)
reconRepo := repository.NewReconciliationRepository(a.db)
// JWT expire hours
jwtExpireHours := a.Cfg.JWT.ExpireHours
if jwtExpireHours == 0 {
jwtExpireHours = 24
}
// Services
a.AdminAuthSvc = service.NewAdminAuthService(adminUserRepo, a.Cfg.JWT.Secret, jwtExpireHours)
a.ChannelSvc = service.NewChannelService(channelCfgRepo, encKey, a.Cfg.Channels)
seqSvc := sequence.NewService(seqRepo)
a.AppSvc = service.NewAppService(appRepo, encKey)
a.NotifySvc = service.NewNotifyService(notifyRepo, tradeRepo, a.Cfg.Notify.HTTPTimeout)
a.MerchantSvc = service.NewMerchantService(merchantRepo, a.ChannelSvc)
a.TradeSvc = service.NewTradeService(tradeRepo, a.ChannelSvc, seqSvc, a.rdb, a.NotifySvc, a.MerchantSvc)
a.RefundSvc = service.NewRefundService(refundRepo, tradeRepo, a.ChannelSvc, seqSvc, a.NotifySvc)
a.MatchSvc = service.NewPaymentMatchService(matchRepo, tradeRepo, a.NotifySvc, a.TradeSvc)
a.ReconSvc = service.NewReconciliationService(reconRepo, tradeRepo, a.ChannelSvc, appRepo)
// 以下 service 目前未直接暴露给 router但已初始化供将来扩展使用
_ = service.NewProfitSharingService(profitSharingRepo, tradeRepo, a.ChannelSvc, seqSvc, a.rdb)
_ = service.NewServiceFeeService(serviceFeeRepo, tradeRepo, a.ChannelSvc)
_ = service.NewWechatService(wechatRepo, encKey)
}