Files
rmser/cmd/main.go
2025-12-18 08:33:12 +03:00

173 lines
5.9 KiB
Go

package main
import (
"log"
"time"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"go.uber.org/zap"
"rmser/config"
"rmser/internal/infrastructure/db"
"rmser/internal/infrastructure/ocr_client"
"rmser/internal/transport/http/middleware"
tgBot "rmser/internal/transport/telegram"
// Repositories
accountPkg "rmser/internal/infrastructure/repository/account"
catalogPkg "rmser/internal/infrastructure/repository/catalog"
draftsPkg "rmser/internal/infrastructure/repository/drafts"
invoicesPkg "rmser/internal/infrastructure/repository/invoices"
ocrRepoPkg "rmser/internal/infrastructure/repository/ocr"
opsRepoPkg "rmser/internal/infrastructure/repository/operations"
recipesPkg "rmser/internal/infrastructure/repository/recipes"
recRepoPkg "rmser/internal/infrastructure/repository/recommendations"
suppliersPkg "rmser/internal/infrastructure/repository/suppliers"
"rmser/internal/infrastructure/rms"
// Services
draftsServicePkg "rmser/internal/services/drafts"
ocrServicePkg "rmser/internal/services/ocr"
recServicePkg "rmser/internal/services/recommend"
"rmser/internal/services/sync"
// Handlers
"rmser/internal/transport/http/handlers"
"rmser/pkg/crypto"
"rmser/pkg/logger"
)
func main() {
// 1. Config
cfg, err := config.LoadConfig(".")
if err != nil {
log.Fatalf("Ошибка загрузки конфига: %v", err)
}
// 2. Logger
logger.Init(cfg.App.Mode)
defer logger.Log.Sync()
logger.Log.Info("Запуск приложения rmser", zap.String("mode", cfg.App.Mode))
// 3. Crypto & DB
if cfg.Security.SecretKey == "" {
logger.Log.Fatal("Security.SecretKey не задан в конфиге!")
}
cryptoManager := crypto.NewCryptoManager(cfg.Security.SecretKey)
database := db.NewPostgresDB(cfg.DB.DSN)
// 4. Repositories
accountRepo := accountPkg.NewRepository(database)
catalogRepo := catalogPkg.NewRepository(database)
recipesRepo := recipesPkg.NewRepository(database)
invoicesRepo := invoicesPkg.NewRepository(database)
opsRepo := opsRepoPkg.NewRepository(database)
recRepo := recRepoPkg.NewRepository(database)
ocrRepo := ocrRepoPkg.NewRepository(database)
draftsRepo := draftsPkg.NewRepository(database)
supplierRepo := suppliersPkg.NewRepository(database)
// 5. RMS Factory
rmsFactory := rms.NewFactory(accountRepo, cryptoManager)
// 6. Services
pyClient := ocr_client.NewClient(cfg.OCR.ServiceURL)
syncService := sync.NewService(rmsFactory, accountRepo, catalogRepo, recipesRepo, invoicesRepo, opsRepo, supplierRepo)
recService := recServicePkg.NewService(recRepo)
ocrService := ocrServicePkg.NewService(ocrRepo, catalogRepo, draftsRepo, accountRepo, pyClient)
draftsService := draftsServicePkg.NewService(draftsRepo, ocrRepo, catalogRepo, accountRepo, supplierRepo, rmsFactory)
// 7. Handlers
draftsHandler := handlers.NewDraftsHandler(draftsService)
ocrHandler := handlers.NewOCRHandler(ocrService)
recommendHandler := handlers.NewRecommendationsHandler(recService)
settingsHandler := handlers.NewSettingsHandler(accountRepo, catalogRepo)
// 8. Telegram Bot (Передаем syncService)
if cfg.Telegram.Token != "" {
// !!! syncService добавлен в аргументы
bot, err := tgBot.NewBot(cfg.Telegram, ocrService, syncService, accountRepo, rmsFactory, cryptoManager)
if err != nil {
logger.Log.Fatal("Ошибка создания Telegram бота", zap.Error(err))
}
go bot.Start()
defer bot.Stop()
}
// 9. HTTP Server
if cfg.App.Mode == "release" {
gin.SetMode(gin.ReleaseMode)
}
r := gin.Default()
corsConfig := cors.DefaultConfig()
corsConfig.AllowAllOrigins = true
corsConfig.AllowMethods = []string{"GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"}
corsConfig.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Authorization", "X-Telegram-User-ID"}
r.Use(cors.New(corsConfig))
api := r.Group("/api")
api.Use(middleware.AuthMiddleware(accountRepo, cfg.Telegram.Token))
{
// Drafts & Invoices
api.GET("/drafts", draftsHandler.GetDrafts)
api.GET("/drafts/:id", draftsHandler.GetDraft)
api.DELETE("/drafts/:id", draftsHandler.DeleteDraft)
// Items CRUD
api.POST("/drafts/:id/items", draftsHandler.AddDraftItem)
api.DELETE("/drafts/:id/items/:itemId", draftsHandler.DeleteDraftItem)
api.PATCH("/drafts/:id/items/:itemId", draftsHandler.UpdateItem)
api.POST("/drafts/:id/commit", draftsHandler.CommitDraft)
api.POST("/drafts/container", draftsHandler.AddContainer)
// Settings
api.GET("/settings", settingsHandler.GetSettings)
api.POST("/settings", settingsHandler.UpdateSettings)
// Dictionaries
api.GET("/dictionaries", draftsHandler.GetDictionaries)
api.GET("/dictionaries/groups", settingsHandler.GetGroupsTree)
api.GET("/dictionaries/stores", draftsHandler.GetStores)
// Recommendations
api.GET("/recommendations", recommendHandler.GetRecommendations)
// OCR & Matching
api.GET("/ocr/catalog", ocrHandler.GetCatalog)
api.GET("/ocr/matches", ocrHandler.GetMatches)
api.POST("/ocr/match", ocrHandler.SaveMatch)
api.DELETE("/ocr/match", ocrHandler.DeleteMatch)
api.GET("/ocr/unmatched", ocrHandler.GetUnmatched)
api.GET("/ocr/search", ocrHandler.SearchProducts)
// Manual Sync Trigger
api.POST("/sync/all", func(c *gin.Context) {
userID := c.MustGet("userID").(uuid.UUID)
// Запускаем в горутине, чтобы не держать соединение
go func() {
if err := syncService.SyncAllData(userID); err != nil {
logger.Log.Error("Manual sync failed", zap.Error(err))
}
}()
c.JSON(200, gin.H{"status": "sync_started", "message": "Синхронизация запущена в фоне"})
})
}
r.GET("/health", func(c *gin.Context) {
c.JSON(200, gin.H{"status": "ok", "time": time.Now().Format(time.RFC3339)})
})
logger.Log.Info("Сервер запускается", zap.String("port", cfg.App.Port))
if err := r.Run(":" + cfg.App.Port); err != nil {
logger.Log.Fatal("Ошибка запуска сервера", zap.Error(err))
}
}