update planned wallets function

This commit is contained in:
NW
2025-01-08 16:20:43 +00:00
parent 66f5251795
commit 5ae148a2ba
7 changed files with 278 additions and 181 deletions

View File

@@ -1,3 +1,5 @@
// database.js
import sqlite3 from 'sqlite3';
import { promisify } from 'util';
import { dirname } from 'path';

View File

@@ -15,37 +15,78 @@ export default class AdminUserHandler {
static async calculateStatistics() {
try {
// Получаем общую статистику по пользователям
const users = await db.allAsync(`
SELECT
u.*,
COUNT(DISTINCT p.id) as total_purchases,
COUNT(DISTINCT cw.id) as total_wallets
FROM users u
LEFT JOIN purchases p ON u.id = p.user_id
LEFT JOIN crypto_wallets cw ON u.id = cw.user_id
LEFT JOIN transactions t ON u.id = t.user_id
GROUP BY u.id
ORDER BY u.created_at DESC
` );
SELECT
u.*,
COUNT(DISTINCT p.id) as total_purchases,
COUNT(DISTINCT cw.id) as total_wallets
FROM users u
LEFT JOIN purchases p ON u.id = p.user_id
LEFT JOIN crypto_wallets cw ON u.id = cw.user_id
LEFT JOIN transactions t ON u.id = t.user_id
GROUP BY u.id
ORDER BY u.created_at DESC
`);
// Calculate general statistics
// Общие метрики
const totalUsers = users.length;
const activeUsers = users.filter(u => u.total_purchases > 0).length;
const totalBalance = users.reduce((sum, u) => sum + (u.total_balance || 0), 0);
const bonusBalance = users.reduce((sum, u) => sum + (u.bonus_balance || 0), 0);
const totalPurchases = users.reduce((sum, u) => sum + (u.total_purchases || 0), 0);
// Create statistics message
// Рассчитываем общий баланс активных и архивных кошельков
let totalActiveWalletsBalance = 0;
let totalArchivedWalletsBalance = 0;
for (const user of users) {
const activeWalletsBalance = await WalletService.getActiveWalletsBalance(user.id);
const archivedWalletsBalance = await WalletService.getArchivedWalletsBalance(user.id);
totalActiveWalletsBalance += activeWalletsBalance;
totalArchivedWalletsBalance += archivedWalletsBalance;
}
// Рассчитываем общий реальный баланс (крипто + бонусы)
const totalRealBalance = totalActiveWalletsBalance + totalArchivedWalletsBalance + bonusBalance;
// Получаем статистику по транзакциям
const totalTransactions = await db.getAsync(`
SELECT COUNT(*) as total_transactions FROM transactions
`);
// Получаем статистику по продуктам
const totalProducts = await db.getAsync(`
SELECT COUNT(*) as total_products FROM products
`);
// Получаем статистику по локациям
const totalLocations = await db.getAsync(`
SELECT COUNT(*) as total_locations FROM locations
`);
// Получаем статистику по категориям
const totalCategories = await db.getAsync(`
SELECT COUNT(*) as total_categories FROM categories
`);
// Формируем сообщение со статистикой
let message = `📊 System Statistics\n\n`;
message += `👥 Total Users: ${totalUsers}\n`;
message += `✅ Active Users: ${activeUsers}\n`;
message += `💰 Bonus Balance: $${bonusBalance.toFixed(2)}\n`;
message += `💰 Total Balance: $${(totalBalance + bonusBalance).toFixed(2)}\n`;
message += `🛍 Total Purchases: ${totalPurchases}`;
message += `💰 Total All Users Balance: $${totalRealBalance.toFixed(2)}\n`;
message += ` ├ Active Wallets Balance: $${totalActiveWalletsBalance.toFixed(2)}\n`;
message += ` ├ Archived Wallets Balance: $${totalArchivedWalletsBalance.toFixed(2)}\n`;
message += ` └ Bonus Balance: $${bonusBalance.toFixed(2)}\n`;
message += `🛍 Total Purchases: ${totalPurchases}\n`;
message += `💸 Total Transactions: ${totalTransactions.total_transactions}\n`;
message += `📦 Total Products: ${totalProducts.total_products}\n`;
message += `📍 Total Locations: ${totalLocations.total_locations}\n`;
message += `📂 Total Categories: ${totalCategories.total_categories}\n`;
return message;
} catch (error) {
return null
console.error('Error in calculateStatistics:', error);
return 'Error loading statistics. Please try again.';
}
}
@@ -58,47 +99,61 @@ export default class AdminUserHandler {
try {
const users = await db.allAsync(`
SELECT
u.*,
COUNT(DISTINCT p.id) as total_purchases,
COUNT(DISTINCT cw.id) as total_wallets
FROM users u
LEFT JOIN purchases p ON u.id = p.user_id
LEFT JOIN crypto_wallets cw ON u.id = cw.user_id
LEFT JOIN transactions t ON u.id = t.user_id
GROUP BY u.id
ORDER BY u.created_at DESC
LIMIT ?
OFFSET ?
`, [limit, offset]);
SELECT
u.*,
COUNT(DISTINCT p.id) as total_purchases,
COUNT(DISTINCT cw.id) as total_wallets
FROM users u
LEFT JOIN purchases p ON u.id = p.user_id
LEFT JOIN crypto_wallets cw ON u.id = cw.user_id
LEFT JOIN transactions t ON u.id = t.user_id
GROUP BY u.id
ORDER BY u.created_at DESC
LIMIT ?
OFFSET ?
`, [limit, offset]);
if ((users.length === 0) && (page == 0)) {
return {text: 'No users registered yet.'};
return { text: 'No users registered yet.' };
}
if ((users.length === 0) && (page > 0)) {
return await this.viewUserPage(page - 1);
}
const statistics = await this.calculateStatistics()
// Calculate balances for each user
const usersWithBalances = await Promise.all(users.map(async (user) => {
const activeWalletsBalance = await WalletService.getActiveWalletsBalance(user.id);
const archivedWalletsBalance = await WalletService.getArchivedWalletsBalance(user.id);
const totalBalance = (user.total_balance || 0) + (user.bonus_balance || 0) + activeWalletsBalance + archivedWalletsBalance;
return {
...user,
activeWalletsBalance,
archivedWalletsBalance,
totalBalance
};
}));
const statistics = await this.calculateStatistics();
const message = `${statistics}\n\nSelect a user from the list below:`;
// Create inline keyboard with user list
const keyboard = {
inline_keyboard: users.map(user => [{
text: `ID: ${user.telegram_id} | Nickname: ${user.username ? "@" + user.username : "None"} | Balance: $${(user.total_balance || 0) + (user.bonus_balance || 0)}`,
inline_keyboard: usersWithBalances.map(user => [{
text: `ID: ${user.telegram_id} | Nickname: ${user.username ? "@" + user.username : "None"} | Balance: $${user.totalBalance.toFixed(2)}`,
callback_data: `view_user_${user.telegram_id}`
}])
};
keyboard.inline_keyboard.push([
{text: `«`, callback_data: `list_users_${previousPage}`},
{text: `»`, callback_data: `list_users_${nextPage}`},
])
{ text: `«`, callback_data: `list_users_${previousPage}` },
{ text: `»`, callback_data: `list_users_${nextPage}` },
]);
return {text: message, markup: keyboard}
return { text: message, markup: keyboard };
} catch (error) {
console.error('Error in handleUserList:', error);
return {text: 'Error loading user list. Please try again.'}
return { text: 'Error loading user list. Please try again.' };
}
}
@@ -185,31 +240,34 @@ export default class AdminUserHandler {
const activeWalletsBalance = await WalletService.getActiveWalletsBalance(user.id);
const archivedWalletsBalance = await WalletService.getArchivedWalletsBalance(user.id);
// Общий баланс (крипто + бонусы + total_balance)
const totalBalance = activeWalletsBalance + archivedWalletsBalance + user.bonus_balance + (user.total_balance || 0);
const message = `
👤 User Profile:
👤 User Profile:
ID: ${telegramId}
📍 Location: ${detailedUser.country || 'Not set'}, ${detailedUser.city || 'Not set'}, ${detailedUser.district || 'Not set'}
ID: ${telegramId}
📍 Location: ${detailedUser.country || 'Not set'}, ${detailedUser.city || 'Not set'}, ${detailedUser.district || 'Not set'}
📊 Activity:
- Total Purchases: ${detailedUser.purchase_count}
- Total Spent: $${detailedUser.total_spent || 0}
- Active Wallets: ${detailedUser.crypto_wallet_count} ($${activeWalletsBalance.toFixed(2)})
- Archived Wallets: ${detailedUser.archived_wallet_count} ($${archivedWalletsBalance.toFixed(2)})
- Bonus Balance: $${user.bonus_balance || 0}
- Total Balance: $${((user.total_balance || 0) + (user.bonus_balance || 0)).toFixed(2)}
📊 Activity:
- Total Purchases: ${detailedUser.purchase_count}
- Total Spent: $${detailedUser.total_spent || 0}
- Active Wallets: ${detailedUser.crypto_wallet_count} ($${activeWalletsBalance.toFixed(2)})
- Archived Wallets: ${detailedUser.archived_wallet_count} ($${archivedWalletsBalance.toFixed(2)})
- Bonus Balance: $${user.bonus_balance || 0}
- Total Balance: $${totalBalance.toFixed(2)}
💰 Recent Transactions (Last 5 of ${transactions.length}):
${transactions.map(t => `$${t.amount} ${t.wallet_type} (${t.tx_hash}) at ${new Date(t.created_at).toLocaleString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })}`).join('\n')}
💰 Recent Transactions (Last 5 of ${transactions.length}):
${transactions.map(t => `$${t.amount} ${t.wallet_type} (${t.tx_hash}) at ${new Date(t.created_at).toLocaleString('en-US', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit' })}`).join('\n')}
🛍 Recent Purchases (Last 5 of ${purchases.length}):
${purchases.map(p => `${p.product_name} x${p.quantity} - $${p.total_price}`).join('\n')}
🛍 Recent Purchases (Last 5 of ${purchases.length}):
${purchases.map(p => `${p.product_name} x${p.quantity} - $${p.total_price}`).join('\n')}
🕒 Pending Purchases:
${pendingPurchases.map(p => `${p.product_name} x${p.quantity} - $${p.total_price}`).join('\n') || ' • No pending purchases'}
🕒 Pending Purchases:
${pendingPurchases.map(p => `${p.product_name} x${p.quantity} - $${p.total_price}`).join('\n') || ' • No pending purchases'}
📅 Registered: ${new Date(detailedUser.created_at).toLocaleString()}
`;
📅 Registered: ${new Date(detailedUser.created_at).toLocaleString()}
`;
const keyboard = {
inline_keyboard: [

View File

@@ -48,7 +48,7 @@ export default class AdminWalletsHandler {
// Удаляем предыдущее сообщение перед отправкой нового
await bot.deleteMessage(chatId, callbackQuery.message.message_id);
// Получаем все кошельки выбранного типа
// Получаем все кошельки выбранного типа (активные и архивные)
const wallets = await WalletService.getWalletsByType(walletType);
if (wallets.length === 0) {
@@ -85,7 +85,8 @@ export default class AdminWalletsHandler {
);
const balances = await walletUtilsInstance.getAllBalances();
const balance = balances[wallet.wallet_type] || { amount: 0, usdValue: 0 };
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
const balance = balances[baseType] || { amount: 0, usdValue: 0 };
walletList += `💰 *${wallet.wallet_type}*\n`;
walletList += `├ Balance: ${balance.amount.toFixed(8)} ${wallet.wallet_type}\n`;
@@ -141,7 +142,8 @@ export default class AdminWalletsHandler {
);
const balances = await walletUtilsInstance.getAllBalances();
const balance = balances[wallet.wallet_type] || { usdValue: 0 };
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
const balance = balances[baseType] || { usdValue: 0 };
totalBalance += balance.usdValue;
}

View File

@@ -1,6 +1,9 @@
// userHandler.js
import config from "../../config/config.js";
import bot from "../../context/bot.js";
import UserService from "../../services/userService.js";
import WalletService from "../../services/walletService.js";
export default class UserHandler {
static async canUseBot(msg) {
@@ -40,26 +43,33 @@ export default class UserHandler {
return;
}
// Получаем балансы активных и архивных кошельков
const activeWalletsBalance = await WalletService.getActiveWalletsBalance(userStats.id);
const archivedWalletsBalance = await WalletService.getArchivedWalletsBalance(userStats.id);
// Общий баланс (крипто + бонусы + total_balance)
const totalBalance = activeWalletsBalance + archivedWalletsBalance + userStats.bonus_balance + (userStats.total_balance || 0);
const locationText = userStats.country && userStats.city && userStats.district
? `${userStats.country}, ${userStats.city}, ${userStats.district}`
: 'Not set';
const text = `
👤 *Your Profile*
👤 *Your Profile*
📱 Telegram ID: \`${telegramId}\`
📍 Location: ${locationText}
📱 Telegram ID: \`${telegramId}\`
📍 Location: ${locationText}
📊 Statistics:
├ Total Purchases: ${userStats.purchase_count || 0}
├ Total Spent: $${userStats.total_spent || 0}
├ Active Wallets: ${userStats.crypto_wallet_count || 0}
├ Archived Wallets: ${userStats.archived_wallet_count || 0}
├ Bonus Balance: $${userStats.bonus_balance || 0}
└ Available Balance: $${(userStats.total_balance || 0) + (userStats.bonus_balance || 0)}
📊 Statistics:
├ Total Purchases: ${userStats.purchase_count || 0}
├ Total Spent: $${userStats.total_spent || 0}
├ Active Wallets: ${userStats.crypto_wallet_count || 0} ($${activeWalletsBalance.toFixed(2)})
├ Archived Wallets: ${userStats.archived_wallet_count || 0} ($${archivedWalletsBalance.toFixed(2)})
├ Bonus Balance: $${userStats.bonus_balance || 0}
└ Available Balance: $${totalBalance.toFixed(2)}
📅 Member since: ${new Date(userStats.created_at).toLocaleDateString()}
`;
📅 Member since: ${new Date(userStats.created_at).toLocaleDateString()}
`;
const keyboard = {
inline_keyboard: [

View File

@@ -1,3 +1,5 @@
// userWalletsHandler.js
import db from '../../config/database.js';
import WalletGenerator from '../../utils/walletGenerator.js';
import WalletUtils from '../../utils/walletUtils.js';
@@ -27,7 +29,7 @@ export default class UserWalletsHandler {
// Получаем активные криптокошельки
const cryptoWallets = await db.allAsync(`
SELECT wallet_type, address
SELECT wallet_type, address, balance
FROM crypto_wallets
WHERE user_id = ?
ORDER BY wallet_type
@@ -42,11 +44,10 @@ export default class UserWalletsHandler {
cryptoWallets.find(w => w.wallet_type === 'ETH')?.address, // ETH address
cryptoWallets.find(w => w.wallet_type === 'USDT')?.address, // USDT address
cryptoWallets.find(w => w.wallet_type === 'USDC')?.address, // USDC address
updatedUser.id,
Date.now() - 30 * 24 * 60 * 60 * 1000
updatedUser.id
);
const balances = await walletUtilsInstance.getAllBalances();
const balances = await walletUtilsInstance.getAllBalancesFromDB();
let totalUsdValue = 0;
// Отображаем активные кошельки
@@ -68,12 +69,12 @@ export default class UserWalletsHandler {
// Бонусный баланс
message += `🎁 *Bonus Balance:* $${updatedUser.bonus_balance.toFixed(2)}\n`;
// Общий баланс (крипто + бонусы)
const totalBalance = totalUsdValue + updatedUser.bonus_balance;
// Общий баланс (крипто + бонусы + total_balance)
const totalBalance = totalUsdValue + updatedUser.bonus_balance + (updatedUser.total_balance || 0);
message += `💰 *Total Balance:* $${totalBalance.toFixed(2)}\n`;
// Доступный баланс (общий баланс минус расходы на покупки)
const availableBalance = updatedUser.total_balance + updatedUser.bonus_balance;
const availableBalance = totalBalance; // Теперь availableBalance включает криптобаланс
message += `💳 *Available Balance:* $${availableBalance.toFixed(2)}\n`;
} else {
message = 'You don\'t have any active wallets yet.';
@@ -112,7 +113,7 @@ export default class UserWalletsHandler {
console.error('Error in showBalance:', error);
await bot.sendMessage(chatId, 'Error loading balance. Please try again.');
}
}
}
static async handleTransactionHistory(callbackQuery, page = 0) {
const chatId = callbackQuery.message.chat.id;
@@ -259,10 +260,10 @@ export default class UserWalletsHandler {
case 'ETH':
address = walletAddresses.eth;
break;
case 'USDT ERC-20':
case 'USDT':
address = walletAddresses.usdt;
break;
case 'USDC ERC-20':
case 'USDC':
address = walletAddresses.usdc;
break;
default:

View File

@@ -22,25 +22,36 @@ class WalletService {
static async getActiveWalletsBalance(userId) {
try {
const wallets = await db.allAsync(
`SELECT wallet_type, address
`SELECT wallet_type, balance
FROM crypto_wallets
WHERE user_id = ? AND wallet_type NOT LIKE '%#_%' ESCAPE '#'`,
[userId]
);
const prices = await WalletUtils.getCryptoPrices();
let totalBalance = 0;
for (const wallet of wallets) {
const walletUtils = new WalletUtils(
wallet.wallet_type === 'BTC' ? wallet.address : null,
wallet.wallet_type === 'LTC' ? wallet.address : null,
wallet.wallet_type === 'ETH' ? wallet.address : null,
wallet.wallet_type === 'USDT' ? wallet.address : null,
wallet.wallet_type === 'USDC' ? wallet.address : null,
userId
);
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
const balance = wallet.balance || 0;
const balances = await walletUtils.getAllBalances();
totalBalance += balances[wallet.wallet_type]?.usdValue || 0;
switch (baseType) {
case 'BTC':
totalBalance += balance * prices.btc;
break;
case 'LTC':
totalBalance += balance * prices.ltc;
break;
case 'ETH':
totalBalance += balance * prices.eth;
break;
case 'USDT':
totalBalance += balance; // USDT is 1:1 with USD
break;
case 'USDC':
totalBalance += balance; // USDC is 1:1 with USD
break;
}
}
return totalBalance;
@@ -53,25 +64,36 @@ class WalletService {
static async getArchivedWalletsBalance(userId) {
try {
const wallets = await db.allAsync(
`SELECT wallet_type, address
`SELECT wallet_type, balance
FROM crypto_wallets
WHERE user_id = ? AND wallet_type LIKE '%#_%' ESCAPE '#'`,
[userId]
);
const prices = await WalletUtils.getCryptoPrices();
let totalBalance = 0;
for (const wallet of wallets) {
const walletUtils = new WalletUtils(
wallet.wallet_type === 'BTC' ? wallet.address : null,
wallet.wallet_type === 'LTC' ? wallet.address : null,
wallet.wallet_type === 'ETH' ? wallet.address : null,
wallet.wallet_type === 'USDT' ? wallet.address : null,
wallet.wallet_type === 'USDC' ? wallet.address : null,
userId
);
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
const balance = wallet.balance || 0;
const balances = await walletUtils.getAllBalances();
totalBalance += balances[wallet.wallet_type]?.usdValue || 0;
switch (baseType) {
case 'BTC':
totalBalance += balance * prices.btc;
break;
case 'LTC':
totalBalance += balance * prices.ltc;
break;
case 'ETH':
totalBalance += balance * prices.eth;
break;
case 'USDT':
totalBalance += balance; // USDT is 1:1 with USD
break;
case 'USDC':
totalBalance += balance; // USDC is 1:1 with USD
break;
}
}
return totalBalance;
@@ -87,8 +109,8 @@ class WalletService {
const wallets = await db.allAsync(
`SELECT *
FROM crypto_wallets
WHERE wallet_type = ?`,
[walletType]
WHERE wallet_type = ? OR wallet_type LIKE ?`,
[walletType, `${walletType}_%`]
);
return wallets;

View File

@@ -1,3 +1,5 @@
// walletUtils.js
import axios from 'axios';
import db from '../config/database.js'; // Импортируем базу данных
@@ -269,8 +271,8 @@ export default class WalletUtils {
BTC: { amount: 0, usdValue: 0 },
LTC: { amount: 0, usdValue: 0 },
ETH: { amount: 0, usdValue: 0 },
'USDT ERC-20': { amount: 0, usdValue: 0 },
'USDC ERC-20': { amount: 0, usdValue: 0 }
USDT: { amount: 0, usdValue: 0 },
USDC: { amount: 0, usdValue: 0 }
};
// Получаем балансы из таблицы crypto_wallets
@@ -279,7 +281,7 @@ export default class WalletUtils {
`, [this.userId]);
for (const wallet of wallets) {
const baseType = WalletUtils.getBaseWalletType(wallet.wallet_type);
const [baseType] = wallet.wallet_type.split('_'); // Учитываем только базовый тип
const balance = wallet.balance || 0;
switch (baseType) {
@@ -296,12 +298,12 @@ export default class WalletUtils {
balances.ETH.usdValue += balance * prices.eth;
break;
case 'USDT':
balances['USDT ERC-20'].amount += balance;
balances['USDT ERC-20'].usdValue += balance;
balances.USDT.amount += balance;
balances.USDT.usdValue += balance;
break;
case 'USDC':
balances['USDC ERC-20'].amount += balance;
balances['USDC ERC-20'].usdValue += balance;
balances.USDC.amount += balance;
balances.USDC.usdValue += balance;
break;
}
}
@@ -345,8 +347,8 @@ export default class WalletUtils {
BTC: { amount: btcBalance, usdValue: btcBalance * prices.btc },
LTC: { amount: ltcBalance, usdValue: ltcBalance * prices.ltc },
ETH: { amount: ethBalance, usdValue: ethBalance * prices.eth },
'USDT ERC-20': { amount: usdtErc20Balance, usdValue: usdtErc20Balance },
'USDC ERC-20': { amount: usdcErc20Balance, usdValue: usdcErc20Balance }
USDT: { amount: usdtErc20Balance, usdValue: usdtErc20Balance },
USDC: { amount: usdcErc20Balance, usdValue: usdcErc20Balance }
};
}
}