mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-05 03:12:34 -06:00
123 lines
4.0 KiB
Go
123 lines
4.0 KiB
Go
package db
|
||
|
||
import (
|
||
"database/sql"
|
||
"fmt"
|
||
"log"
|
||
"os"
|
||
"regexp"
|
||
"rmser/internal/domain/account"
|
||
"rmser/internal/domain/catalog"
|
||
"rmser/internal/domain/drafts"
|
||
"rmser/internal/domain/invoices"
|
||
"rmser/internal/domain/ocr"
|
||
"rmser/internal/domain/operations"
|
||
"rmser/internal/domain/recipes"
|
||
"rmser/internal/domain/recommendations"
|
||
"rmser/internal/domain/suppliers"
|
||
"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(
|
||
&account.User{},
|
||
&account.RMSServer{},
|
||
&catalog.Product{},
|
||
&catalog.MeasureUnit{},
|
||
&catalog.ProductContainer{},
|
||
&catalog.Store{},
|
||
&suppliers.Supplier{},
|
||
&recipes.Recipe{},
|
||
&recipes.RecipeItem{},
|
||
&invoices.Invoice{},
|
||
&invoices.InvoiceItem{},
|
||
&drafts.DraftInvoice{},
|
||
&drafts.DraftInvoiceItem{},
|
||
&operations.StoreOperation{},
|
||
&recommendations.Recommendation{},
|
||
&ocr.ProductMatch{},
|
||
&ocr.UnmatchedItem{},
|
||
)
|
||
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)
|
||
}
|
||
}
|