// userService.js import db from "../config/database.js"; import Wallet from "../models/Wallet.js"; import WalletUtils from "../utils/walletUtils.js"; class UserService { // Функция для нормализации telegram_id static normalizeTelegramId(telegramId) { if (typeof telegramId === 'number') { // Если это число, преобразуем его в строку и удаляем ".0" return telegramId.toString().replace(/\.0$/, ''); } // Если это уже строка, возвращаем как есть return telegramId.toString(); } // Функция для валидации telegram_id static validateTelegramId(telegramId) { if (typeof telegramId !== 'string') { throw new Error('telegram_id должен быть строкой'); } if (telegramId.includes('.0')) { throw new Error('telegram_id не должен содержать ".0"'); } } static async createUser(userData) { try { // Нормализуем и валидируем telegram_id const normalizedTelegramId = this.normalizeTelegramId(userData?.telegram_id); // console.log("Normalized telegram_id:", normalizedTelegramId); // Отладочный вывод this.validateTelegramId(normalizedTelegramId); // Обновляем значение telegram_id в объекте userData userData.telegram_id = normalizedTelegramId; // Проверяем, существует ли пользователь с таким telegram_id const existingUser = await this.getUserByTelegramId(normalizedTelegramId); if (existingUser) { console.log("User already exists with telegram_id:", normalizedTelegramId); return existingUser.id; } // Подготавливаем данные для вставки в базу данных const fields = Object.keys(userData); const values = Object.values(userData); const marks = Array(fields.length).fill('?'); const query = ` INSERT INTO users (${fields.join(', ')}) VALUES (${marks.join(', ')}) `; // Выполняем запрос к базе данных await db.runAsync('BEGIN TRANSACTION'); const result = await db.runAsync(query, values); await db.runAsync('COMMIT'); return result.lastID; } catch (error) { await db.runAsync('ROLLBACK'); console.error('Error creating user:', error); throw error; } } static async getUserByUserId(userId) { try { return await db.getAsync( 'SELECT * FROM users WHERE id = ?', [String(userId)] ); } catch (error) { console.error('Error getting user:', error); throw error; } } static async getUserByTelegramId(telegramId) { try { const normalizedTelegramId = this.normalizeTelegramId(telegramId); return await db.getAsync( 'SELECT * FROM users WHERE telegram_id = ?', [normalizedTelegramId] ); } catch (error) { console.error('Error getting user:', error); throw error; } } static async getDetailedUserByTelegramId(telegramId) { try { const normalizedTelegramId = this.normalizeTelegramId(telegramId); return await db.getAsync(` SELECT u.*, COUNT(DISTINCT p.id) as purchase_count, (SELECT COALESCE(SUM(p2.total_price), 0) FROM purchases p2 WHERE p2.user_id = u.id) as total_spent, COUNT(DISTINCT cw.id) as crypto_wallet_count, COUNT(DISTINCT cw2.id) as archived_wallet_count FROM users u LEFT JOIN purchases p ON u.id = p.user_id LEFT JOIN crypto_wallets cw ON u.id = cw.user_id AND cw.wallet_type NOT LIKE '%#_%' ESCAPE '#' LEFT JOIN crypto_wallets cw2 ON u.id = cw2.user_id AND cw2.wallet_type LIKE '%#_%' ESCAPE '#' WHERE u.telegram_id = ? GROUP BY u.id `, [normalizedTelegramId]); } catch (error) { console.error('Error getting user stats:', error); throw error; } } static async updateUser(userId, newUserData) {} static async deleteUser() {} static async recalculateUserBalanceByTelegramId(telegramId) { const normalizedTelegramId = this.normalizeTelegramId(telegramId); const user = await this.getUserByTelegramId(normalizedTelegramId); if (!user) { return; } try { // Получаем все крипто-балансы пользователя const cryptoBalances = await db.allAsync( `SELECT wallet_type, balance FROM crypto_wallets WHERE user_id = ?`, [user.id] ); // Получаем актуальные курсы криптовалют const prices = await WalletUtils.getCryptoPrices(); // Пересчитываем балансы в доллары let totalCryptoBalance = 0; for (const wallet of cryptoBalances) { const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type); // Убираем суффиксы const rate = prices[baseType.toLowerCase()] || 0; // Получаем курс для базового типа totalCryptoBalance += wallet.balance * rate; } // Получаем сумму всех покупок в крипте const cryptoPurchases = await db.getAsync( `SELECT SUM(total_price) as total_sum FROM purchases WHERE user_id = ? AND wallet_type LIKE 'crypto%'`, [user.id] ); // Вычитаем сумму покупок из общего крипто-баланса const remainingBalance = totalCryptoBalance - (cryptoPurchases?.total_sum || 0); // Обновляем поле total_balance в таблице users await db.runAsync( `UPDATE users SET total_balance = ? WHERE id = ?`, [remainingBalance, user.id] ); console.log(`[DEBUG] Updated total_balance for user ${user.id}: ${remainingBalance}`); } catch (error) { console.error('Error recalculating user balance:', error); throw error; } } static async updateUserLocation(telegramId, country, city, district) { const normalizedTelegramId = this.normalizeTelegramId(telegramId); await db.runAsync( 'UPDATE users SET country = ?, city = ?, district = ? WHERE telegram_id = ?', [country, city, district, normalizedTelegramId] ); } static async updateUserStatus(telegramId, status) { const normalizedTelegramId = this.normalizeTelegramId(telegramId); try { await db.runAsync('BEGIN TRANSACTION'); // Update user status await db.runAsync('UPDATE users SET status = ? WHERE telegram_id = ?', [status, normalizedTelegramId]); // Commit transaction await db.runAsync('COMMIT'); } catch (e) { await db.runAsync("ROLLBACK"); console.error('Error deleting user:', e); throw e; } } static async getUserBalance(userId) { try { const user = await this.getUserByUserId(userId); if (!user) { throw new Error('User not found'); } // Возвращаем сумму доступного крипто-баланса и бонусного баланса return user.total_balance + user.bonus_balance; } catch (error) { console.error('Error getting user balance:', error); throw error; } } } export default UserService;