main.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "log"
  6. "time"
  7. "spider/internal/config"
  8. "spider/internal/handler"
  9. "spider/internal/llm"
  10. "spider/internal/model"
  11. "spider/internal/search"
  12. "spider/internal/service"
  13. "spider/internal/telegram"
  14. "spider/internal/worker"
  15. "github.com/redis/go-redis/v9"
  16. "gorm.io/driver/mysql"
  17. "gorm.io/gorm"
  18. )
  19. func main() {
  20. // 1. 加载配置
  21. cfg, err := config.Load("configs/config.yaml")
  22. if err != nil {
  23. log.Fatalf("load config: %v", err)
  24. }
  25. // 2. 连接 MySQL
  26. dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
  27. cfg.MySQL.User, cfg.MySQL.Password, cfg.MySQL.Host, cfg.MySQL.Port, cfg.MySQL.Database)
  28. db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  29. if err != nil {
  30. log.Fatalf("connect mysql: %v", err)
  31. }
  32. // 3. AutoMigrate 所有表
  33. err = db.AutoMigrate(
  34. &model.ManagedSeed{},
  35. &model.ManagedKeyword{},
  36. &model.ManagedSetting{},
  37. &model.Channel{},
  38. &model.NavSite{},
  39. &model.MerchantRaw{},
  40. &model.MerchantClean{},
  41. &model.Task{},
  42. &model.ConfigRevision{},
  43. )
  44. if err != nil {
  45. log.Fatalf("automigrate: %v", err)
  46. }
  47. log.Println("MySQL tables migrated")
  48. // 3a. 初始化 managed_settings 默认值(幂等,已有记录不覆盖)
  49. seedSettings(db)
  50. // 4. 连接 Redis
  51. rdb := redis.NewClient(&redis.Options{
  52. Addr: fmt.Sprintf("%s:%d", cfg.Redis.Host, cfg.Redis.Port),
  53. Password: cfg.Redis.Password,
  54. DB: cfg.Redis.DB,
  55. })
  56. log.Println("Redis connected")
  57. // 5. 初始化 TaskService
  58. taskSvc := service.NewTaskService(db, rdb)
  59. // 5a. 初始化 SettingsService 并加载到 Redis 缓存
  60. settings := service.NewSettingsService(db, rdb)
  61. if err := settings.Load(context.Background()); err != nil {
  62. log.Printf("load settings into cache: %v", err)
  63. }
  64. // 5b. 初始化 AccountManager(账号从配置读取,为空时运行时从 DB 动态加载)
  65. tgAccounts := make([]telegram.Account, 0, len(cfg.Telegram.Accounts))
  66. for _, a := range cfg.Telegram.Accounts {
  67. tgAccounts = append(tgAccounts, telegram.Account{
  68. Phone: a.Phone,
  69. SessionFile: a.SessionFile,
  70. AppID: cfg.Telegram.AppID,
  71. AppHash: cfg.Telegram.AppHash,
  72. })
  73. }
  74. tgManager := telegram.NewAccountManager(tgAccounts, rdb)
  75. // 5c. 初始化 LLM Client(配置缺失时为 nil,phase 会安全跳过)
  76. var llmClient *llm.Client
  77. if cfg.LLM.APIKey != "" {
  78. llmClient = llm.New(cfg.LLM.BaseURL, cfg.LLM.APIKey, cfg.LLM.Model, 30*time.Second)
  79. }
  80. // 5d. 初始化 Serper Client(配置缺失时为 nil)
  81. var serperClient *search.SerperClient
  82. if cfg.Serper.APIKey != "" {
  83. serperClient = search.NewSerperClient(cfg.Serper.APIKey, cfg.Serper.ResultsPerPage, cfg.Serper.MaxPages)
  84. }
  85. // 6. 初始化并启动 asynq Worker
  86. redisAddr := fmt.Sprintf("%s:%d", cfg.Redis.Host, cfg.Redis.Port)
  87. w := worker.New(redisAddr, cfg.Redis.Password, cfg.Redis.DB, db, rdb, tgManager, llmClient, settings, serperClient, cfg.GitHub.Token)
  88. go func() {
  89. log.Println("asynq worker starting...")
  90. if err := w.Start(); err != nil {
  91. log.Fatalf("asynq worker error: %v", err)
  92. }
  93. }()
  94. // 7. 初始化 Gin router
  95. r := handler.SetupRouter(db, rdb, taskSvc)
  96. addr := handler.ServerAddr(cfg.Server.Port)
  97. log.Printf("Server starting on %s", addr)
  98. if err := r.Run(addr); err != nil {
  99. log.Fatalf("gin run: %v", err)
  100. }
  101. }