This commit is contained in:
2026-03-13 15:51:59 +08:00
parent 4db2386bbf
commit 4e91f4cede
133 changed files with 19502 additions and 37 deletions

126
backend/internal/app/app.go Normal file
View File

@@ -0,0 +1,126 @@
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)
}