mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
start rmser
This commit is contained in:
110
internal/infrastructure/db/postgres.go
Normal file
110
internal/infrastructure/db/postgres.go
Normal file
@@ -0,0 +1,110 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"rmser/internal/domain/catalog"
|
||||
"rmser/internal/domain/invoices"
|
||||
"rmser/internal/domain/ocr"
|
||||
"rmser/internal/domain/operations"
|
||||
"rmser/internal/domain/recipes"
|
||||
"rmser/internal/domain/recommendations"
|
||||
"time"
|
||||
|
||||
_ "github.com/jackc/pgx/v5/stdlib"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
func NewPostgresDB(dsn string) *gorm.DB {
|
||||
// 1. Проверка и создание БД перед основным подключением
|
||||
ensureDBExists(dsn)
|
||||
|
||||
// 2. Настройка логгера GORM
|
||||
newLogger := logger.New(
|
||||
log.New(os.Stdout, "\r\n", log.LstdFlags),
|
||||
logger.Config{
|
||||
SlowThreshold: time.Second,
|
||||
LogLevel: logger.Warn,
|
||||
IgnoreRecordNotFoundError: true,
|
||||
Colorful: true,
|
||||
},
|
||||
)
|
||||
|
||||
// 3. Основное подключение
|
||||
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{
|
||||
Logger: newLogger,
|
||||
})
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("не удалось подключиться к БД: %v", err))
|
||||
}
|
||||
|
||||
// 4. Автомиграция
|
||||
err = db.AutoMigrate(
|
||||
&catalog.Product{},
|
||||
&recipes.Recipe{},
|
||||
&recipes.RecipeItem{},
|
||||
&invoices.Invoice{},
|
||||
&invoices.InvoiceItem{},
|
||||
&operations.StoreOperation{},
|
||||
&recommendations.Recommendation{},
|
||||
&ocr.ProductMatch{},
|
||||
)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("ошибка миграции БД: %v", err))
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
// ensureDBExists подключается к системной БД 'postgres' и создает целевую, если её нет
|
||||
func ensureDBExists(fullDSN string) {
|
||||
// Регулярка для извлечения имени базы из DSN (ищем dbname=... )
|
||||
re := regexp.MustCompile(`dbname=([^\s]+)`)
|
||||
matches := re.FindStringSubmatch(fullDSN)
|
||||
|
||||
if len(matches) < 2 {
|
||||
// Если не нашли dbname, возможно формат URL (postgres://...),
|
||||
// пропускаем авто-создание, полагаемся на ошибку драйвера
|
||||
return
|
||||
}
|
||||
|
||||
targetDB := matches[1]
|
||||
|
||||
// Заменяем целевую БД на системную 'postgres' для подключения
|
||||
maintenanceDSN := re.ReplaceAllString(fullDSN, "dbname=postgres")
|
||||
|
||||
// Используем стандартный sql драйвер через pgx (который под капотом у gorm/postgres)
|
||||
// Важно: нам не нужен GORM здесь, нужен чистый SQL для CREATE DATABASE
|
||||
db, err := sql.Open("pgx", maintenanceDSN)
|
||||
if err != nil {
|
||||
// Если не вышло подключиться к postgres, просто выходим,
|
||||
// основная ошибка вылетит при попытке gorm.Open
|
||||
log.Printf("[WARN] Не удалось подключиться к системной БД для проверки: %v", err)
|
||||
return
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
// Проверяем существование базы
|
||||
var exists bool
|
||||
checkSQL := fmt.Sprintf("SELECT EXISTS(SELECT 1 FROM pg_database WHERE datname = '%s')", targetDB)
|
||||
err = db.QueryRow(checkSQL).Scan(&exists)
|
||||
if err != nil {
|
||||
log.Printf("[WARN] Ошибка проверки существования БД: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !exists {
|
||||
log.Printf("[INFO] База данных '%s' не найдена. Создаю...", targetDB)
|
||||
// CREATE DATABASE не может быть выполнен в транзакции, поэтому Exec
|
||||
_, err = db.Exec(fmt.Sprintf("CREATE DATABASE \"%s\"", targetDB))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("не удалось создать базу данных %s: %v", targetDB, err))
|
||||
}
|
||||
log.Printf("[INFO] База данных '%s' успешно создана", targetDB)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user