package main import ( "errors" "log" "time" "github.com/gin-gonic/gin" "github.com/glebarez/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" "github.com/wangjia/pay/config" "github.com/wangjia/pay/internal/channel" "github.com/wangjia/pay/internal/model" "github.com/wangjia/pay/internal/router" ) func main() { config.Load() db := initDB() autoMigrate(db) seed(db) reg := channel.NewRegistry(db) gin.SetMode(config.C.Server.Mode) r := gin.New() r.Use(gin.Logger(), gin.Recovery()) orderSvc := router.Setup(r, db, reg) if config.C.QuerySync.Enabled { orderSvc.StartQuerySync( time.Duration(config.C.QuerySync.IntervalSec)*time.Second, time.Duration(config.C.QuerySync.MaxAgeMin)*time.Minute, ) log.Printf("查单兜底已启动:每 %ds 一次", config.C.QuerySync.IntervalSec) } addr := ":" + config.C.Server.Port log.Printf("支付服务启动 %s (mode=%s, base_url=%s)", addr, config.C.Server.Mode, config.C.Server.BaseURL) if err := r.Run(addr); err != nil { log.Fatalf("启动失败: %v", err) } } func initDB() *gorm.DB { logLevel := logger.Silent if config.C.Server.Mode == "debug" { logLevel = logger.Info } gormCfg := &gorm.Config{Logger: logger.Default.LogMode(logLevel), TranslateError: true} switch config.C.Database.Driver { case "sqlite", "": db, err := gorm.Open(sqlite.Open(config.C.Database.DSN), gormCfg) if err != nil { log.Fatalf("连接 sqlite 失败: %v", err) } return db case "mysql": log.Fatal("mysql 驱动尚未启用:go get gorm.io/driver/mysql 后在 initDB 接入") return nil default: log.Fatalf("未知 database.driver: %s", config.C.Database.Driver) return nil } } func autoMigrate(db *gorm.DB) { if err := db.AutoMigrate( &model.Merchant{}, &model.Product{}, &model.Order{}, &model.NotifyLog{}, ); err != nil { log.Fatalf("自动迁移失败: %v", err) } log.Println("AutoMigrate 完成") } // seed 据 config 的 alipay_sandbox upsert 一个支付宝商户,并为其补两个测试套餐。 func seed(db *gorm.DB) { sb := config.C.AlipaySandbox if !sb.Enabled { log.Println("[seed] alipay_sandbox.enabled=false,跳过沙箱商户初始化") return } var m model.Merchant err := db.Where("code = ?", sb.MerchantCode).First(&m).Error if errors.Is(err, gorm.ErrRecordNotFound) { m = model.Merchant{ Code: sb.MerchantCode, Name: sb.MerchantName, Channel: "alipay", } } else if err != nil { log.Fatalf("[seed] 查询商户失败: %v", err) } m.Production = sb.Production m.Enabled = true m.AppID = sb.AppID m.AppPrivateKey = sb.AppPrivateKey m.AlipayPublicKey = sb.AlipayPublicKey if err := db.Save(&m).Error; err != nil { log.Fatalf("[seed] 保存商户失败: %v", err) } log.Printf("[seed] 支付宝沙箱商户就绪: code=%s app_id=%s", m.Code, m.AppID) var cnt int64 db.Model(&model.Product{}).Where("merchant_id = ?", m.ID).Count(&cnt) if cnt == 0 { samples := []model.Product{ {MerchantID: m.ID, Name: "测试套餐 A", Description: "沙箱联调用", Price: "0.01", Active: true, Sort: 1}, {MerchantID: m.ID, Name: "测试套餐 B", Description: "沙箱联调用", Price: "0.02", Active: true, Sort: 2}, } if err := db.Create(&samples).Error; err != nil { log.Fatalf("[seed] 创建测试套餐失败: %v", err) } log.Printf("[seed] 已为商户 %s 创建 %d 个测试套餐", m.Code, len(samples)) } }