2025-12-13 13:27:05 +00:00
import ' package:drift/drift.dart ' ;
import ' package:drift_postgres/drift_postgres.dart ' ;
import ' package:postgres/postgres.dart ' as pg ;
import ' dart:io ' ;
// Импорт конвертеров (нужен для генерации кода)
import ' converters.dart ' ;
// Импорт всех таблиц
import ' tables/users.dart ' ;
import ' tables/auth.dart ' ;
import ' tables/packs.dart ' ;
import ' tables/relations.dart ' ;
import ' tables/subscriptions.dart ' ;
import ' tables/payments.dart ' ;
import ' tables/tests.dart ' ;
import ' tables/tasks.dart ' ;
import ' tables/promo_codes.dart ' ;
import ' tables/discounts.dart ' ;
import ' tables/statistics.dart ' ;
import ' tables/telegram.dart ' ;
import ' tables/achievements.dart ' ;
// Импорт DAOs
import ' daos/user_dao.dart ' ;
import ' daos/pack_dao.dart ' ;
import ' daos/test_dao.dart ' ;
import ' daos/payment_dao.dart ' ;
import ' daos/subscription_dao.dart ' ;
import ' daos/task_dao.dart ' ;
import ' daos/promo_code_dao.dart ' ;
import ' daos/discount_dao.dart ' ;
import ' daos/statistics_dao.dart ' ;
import ' daos/achievement_dao.dart ' ;
2025-12-13 21:40:59 +00:00
import ' postgres_constants.dart ' ;
2025-12-13 13:27:05 +00:00
// Сгенерированный код будет здесь
part ' database.g.dart ' ;
@ DriftDatabase (
tables: [
// User tables
Users ,
UserDatas ,
// Auth tables
Tokens ,
RefreshTokens ,
TelegramAuthCodes ,
// Pack tables
CardPacks ,
GameCards ,
VoiceModels ,
// Relations
UserPacks ,
PreviewCards ,
CardPackCards ,
CardVoices ,
// Subscription tables
SubscriptionPlans ,
UserSubscriptions ,
// Payment tables
Payments ,
// Test tables
Tests ,
TestQuestions ,
TestPackRelations ,
TestStatistics ,
// Task tables
Tasks ,
UserTasks ,
UserTaskProgresses ,
UserTaskResults ,
// Promo code tables
PromoCodesCampaigns ,
PromoCodes ,
// Discount tables
DiscountCampaigns ,
Discounts ,
DiscountUserDatas ,
// Statistics tables
StudySessions ,
// Achievements tables
UserAchievements ,
// Telegram tables
ShareRequests ,
] ,
daos: [
UserDao ,
PackDao ,
TestDao ,
PaymentDao ,
SubscriptionDao ,
TaskDao ,
PromoCodeDao ,
DiscountDao ,
StatisticsDao ,
AchievementDao ,
] ,
)
class AppDatabase extends _ $AppDatabase {
AppDatabase ( super . e ) ;
@ override
int get schemaVersion = > 1 ;
/// Factory для подключения к PostgreSQL
static AppDatabase connect ( {
required String host ,
required int port ,
required String database ,
required String username ,
required String password ,
bool useSsl = false ,
} ) {
final endpoint = pg . Endpoint (
host: host ,
port: port ,
database: database ,
username: username ,
password: password ,
) ;
final connection = PgDatabase (
endpoint: endpoint ,
settings: pg . ConnectionSettings (
sslMode: useSsl ? pg . SslMode . require : pg . SslMode . disable ,
connectTimeout: const Duration ( seconds: 10 ) ,
) ,
) ;
return AppDatabase ( connection ) ;
}
/// Factory для подключения из environment variables
static AppDatabase fromEnvironment ( ) {
return connect (
host: Platform . environment [ ' DB_HOST ' ] ? ? ' localhost ' ,
port: int . parse ( Platform . environment [ ' DB_PORT ' ] ? ? ' 5432 ' ) ,
database: Platform . environment [ ' DB_NAME ' ] ? ? ' mnemo_cards_dev ' ,
username: Platform . environment [ ' DB_USER ' ] ? ? ' mnemo_user ' ,
password: Platform . environment [ ' DB_PASSWORD ' ] ? ? ' ' ,
useSsl: Platform . environment [ ' DB_SSL_MODE ' ] = = ' require ' ,
) ;
}
@ override
MigrationStrategy get migration = > MigrationStrategy (
onCreate: ( Migrator m ) async {
print ( ' Creating database schema... ' ) ;
await m . createAll ( ) ;
print ( ' Database schema created successfully ' ) ;
// Создать индексы для оптимизации
await _createIndexes ( ) ;
} ,
onUpgrade: ( Migrator m , int from , int to ) async {
print ( ' Migrating database from version $ from to $ to ' ) ;
// Миграции при обновлении схемы
// if (from < 2) {
// await m.addColumn(users, users.phoneNumber);
// }
} ,
beforeOpen: ( details ) async {
print ( ' Opening database connection... ' ) ;
// Проверка подключения
final result = await customSelect ( ' SELECT 1 as test ' ) . getSingle ( ) ;
print ( ' Database connection successful: ${ result . data } ' ) ;
// Включить foreign key constraints
await customStatement ( ' SET CONSTRAINTS ALL IMMEDIATE ' ) ;
} ,
) ;
/// Создание индексов для оптимизации запросов
Future < void > _createIndexes ( ) async {
print ( ' Creating indexes... ' ) ;
// Users indexes
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_users_email ON users(email) WHERE email IS NOT NULL ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_users_admin ON users(admin) WHERE admin = true ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_users_not_deleted ON users(is_deleted) WHERE is_deleted = false ' ) ;
// UserDatas indexes
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_user_datas_user_id ON user_datas(user_id) ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_user_datas_last_online ON user_datas(last_time_online DESC) ' ) ;
// Auth indexes
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_tokens_user_id ON tokens(user_id) ' ) ;
2025-12-13 22:03:37 +00:00
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_tokens_expires ON tokens(expires) WHERE expires > EXTRACT(EPOCH FROM NOW())::BIGINT ' ) ;
2025-12-13 13:27:05 +00:00
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_refresh_tokens_user_id ON refresh_tokens(user_id) ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_refresh_tokens_jti ON refresh_tokens(jti) ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_refresh_tokens_not_blacklisted ON refresh_tokens(is_blacklisted) WHERE is_blacklisted = false ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_telegram_codes_code ON telegram_auth_codes(code) ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_telegram_codes_not_used ON telegram_auth_codes(is_used) WHERE is_used = false ' ) ;
// CardPacks indexes
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_packs_enabled ON card_packs(enabled) WHERE enabled = true ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_packs_order ON card_packs("order") ' ) ;
// GameCards indexes
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_cards_original ON game_cards(original) ' ) ;
// UserPacks indexes
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_user_packs_user_id ON user_packs(user_id) ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_user_packs_pack_id ON user_packs(pack_id) ' ) ;
// Payments indexes
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_payments_user_id ON payments(user_id) ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_payments_status ON payments(status) ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_payments_created ON payments(date DESC) ' ) ;
// Subscriptions indexes
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_subscriptions_user_id ON user_subscriptions(user_id) ' ) ;
2025-12-13 22:03:37 +00:00
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_subscriptions_active ON user_subscriptions(finish) WHERE finish > EXTRACT(EPOCH FROM NOW())::BIGINT ' ) ;
2025-12-13 13:27:05 +00:00
// Tests indexes
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_test_questions_test_id ON test_questions(test_id) ' ) ;
// StudySessions indexes
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON study_sessions(user_id) ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_sessions_started ON study_sessions(start_time DESC) ' ) ;
// PromoCodes indexes
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_promo_codes_code ON promo_codes(code) ' ) ;
await customStatement ( ' CREATE INDEX IF NOT EXISTS idx_promo_codes_campaign_id ON promo_codes(campaign_id) ' ) ;
print ( ' Indexes created successfully ' ) ;
}
}