Merge pull request 'feature/user-section' (#26) from feature/user-section into main
Reviewed-on: https://git.softuniq.eu/Telegram-Market/telegram-shop/pulls/26
This commit is contained in:
commit
56698c28c4
@ -95,6 +95,8 @@ const initDb = async () => {
|
|||||||
city TEXT,
|
city TEXT,
|
||||||
district TEXT,
|
district TEXT,
|
||||||
status INTEGER DEFAULT 0,
|
status INTEGER DEFAULT 0,
|
||||||
|
total_balance REAL DEFAULT 0,
|
||||||
|
bonus_balance REAL DEFAULT 0,
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
)
|
)
|
||||||
`);
|
`);
|
||||||
|
@ -370,6 +370,10 @@ export default class AdminProductHandler {
|
|||||||
text: 'No products for this location',
|
text: 'No products for this location',
|
||||||
markup: {
|
markup: {
|
||||||
inline_keyboard: [
|
inline_keyboard: [
|
||||||
|
[{
|
||||||
|
text: '📥 Import Products',
|
||||||
|
callback_data: `add_product_${locationId}_${categoryId}_${subcategoryId}`
|
||||||
|
}],
|
||||||
[{text: '« Back', callback_data: `prod_category_${locationId}_${categoryId}`}]
|
[{text: '« Back', callback_data: `prod_category_${locationId}_${categoryId}`}]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
11
src/index.js
11
src/index.js
@ -239,6 +239,9 @@ bot.on('callback_query', async (callbackQuery) => {
|
|||||||
} else if (action.startsWith('buy_product_')) {
|
} else if (action.startsWith('buy_product_')) {
|
||||||
logDebug(action, 'handleBuyProduct');
|
logDebug(action, 'handleBuyProduct');
|
||||||
await userProductHandler.handleBuyProduct(callbackQuery);
|
await userProductHandler.handleBuyProduct(callbackQuery);
|
||||||
|
} else if (action.startsWith('pay_with_')) {
|
||||||
|
logDebug(action, 'handlePay');
|
||||||
|
await userProductHandler.handlePay(callbackQuery);
|
||||||
}
|
}
|
||||||
// Admin location management
|
// Admin location management
|
||||||
else if (action === 'add_location') {
|
else if (action === 'add_location') {
|
||||||
@ -287,7 +290,7 @@ bot.on('callback_query', async (callbackQuery) => {
|
|||||||
logDebug(action, 'handleSubcategorySelection');
|
logDebug(action, 'handleSubcategorySelection');
|
||||||
await adminProductHandler.handleSubcategorySelection(callbackQuery);
|
await adminProductHandler.handleSubcategorySelection(callbackQuery);
|
||||||
} else if (action.startsWith('list_products_')) {
|
} else if (action.startsWith('list_products_')) {
|
||||||
logDebug(action, 'handleSubcategorySelection');
|
logDebug(action, 'handleProductListPage');
|
||||||
await adminProductHandler.handleProductListPage(callbackQuery);
|
await adminProductHandler.handleProductListPage(callbackQuery);
|
||||||
} else if (action.startsWith('add_product_')) {
|
} else if (action.startsWith('add_product_')) {
|
||||||
logDebug(action, 'handleAddProduct');
|
logDebug(action, 'handleAddProduct');
|
||||||
@ -299,7 +302,7 @@ bot.on('callback_query', async (callbackQuery) => {
|
|||||||
logDebug(action, 'handleProductEdit');
|
logDebug(action, 'handleProductEdit');
|
||||||
await adminProductHandler.handleProductEdit(callbackQuery)
|
await adminProductHandler.handleProductEdit(callbackQuery)
|
||||||
} else if (action.startsWith('delete_product_')) {
|
} else if (action.startsWith('delete_product_')) {
|
||||||
logDebug(action, 'handleViewProduct');
|
logDebug(action, 'handleProductDelete');
|
||||||
await adminProductHandler.handleProductDelete(callbackQuery);
|
await adminProductHandler.handleProductDelete(callbackQuery);
|
||||||
} else if (action.startsWith('confirm_delete_product_')) {
|
} else if (action.startsWith('confirm_delete_product_')) {
|
||||||
logDebug(action, 'handleConfirmDelete');
|
logDebug(action, 'handleConfirmDelete');
|
||||||
@ -310,13 +313,13 @@ bot.on('callback_query', async (callbackQuery) => {
|
|||||||
logDebug(action, 'handleViewUser');
|
logDebug(action, 'handleViewUser');
|
||||||
await adminUserHandler.handleViewUser(callbackQuery);
|
await adminUserHandler.handleViewUser(callbackQuery);
|
||||||
} else if (action.startsWith('list_users_')) {
|
} else if (action.startsWith('list_users_')) {
|
||||||
logDebug(action, 'handleViewUser');
|
logDebug(action, 'handleUserListPage');
|
||||||
await adminUserHandler.handleUserListPage(callbackQuery);
|
await adminUserHandler.handleUserListPage(callbackQuery);
|
||||||
} else if (action.startsWith('delete_user_')) {
|
} else if (action.startsWith('delete_user_')) {
|
||||||
logDebug(action, 'handleDeleteUser');
|
logDebug(action, 'handleDeleteUser');
|
||||||
await adminUserHandler.handleDeleteUser(callbackQuery);
|
await adminUserHandler.handleDeleteUser(callbackQuery);
|
||||||
} else if (action.startsWith('block_user_')) {
|
} else if (action.startsWith('block_user_')) {
|
||||||
logDebug(action, 'handleDeleteUser');
|
logDebug(action, 'handleBlockUser');
|
||||||
await adminUserHandler.handleBlockUser(callbackQuery);
|
await adminUserHandler.handleBlockUser(callbackQuery);
|
||||||
} else if (action.startsWith('confirm_delete_user_')) {
|
} else if (action.startsWith('confirm_delete_user_')) {
|
||||||
logDebug(action, 'handleConfirmDelete');
|
logDebug(action, 'handleConfirmDelete');
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import db from '../config/database.js';
|
import db from '../config/database.js';
|
||||||
|
import Wallet from "./Wallet.js";
|
||||||
|
|
||||||
export default class User {
|
export default class User {
|
||||||
static async create(telegramId, username) {
|
static async create(telegramId, username) {
|
||||||
@ -103,4 +104,24 @@ export default class User {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async recalculateBalance(telegramId) {
|
||||||
|
const user = await User.getById(telegramId);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const archivedBalance = await Wallet.getArchivedWalletsBalance(user.id);
|
||||||
|
const activeBalance = await Wallet.getActiveWalletsBalance(user.id);
|
||||||
|
|
||||||
|
const purchases = await db.getAsync(
|
||||||
|
`SELECT SUM(total_price) as total_sum FROM purchases WHERE user_id = ?`,
|
||||||
|
[user.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
const userTotalBalance = (activeBalance + archivedBalance) - (purchases?.total_sum || 0);
|
||||||
|
|
||||||
|
await db.runAsync(`UPDATE users SET total_balance = ? WHERE id = ?`, [userTotalBalance, user.id]);
|
||||||
|
}
|
||||||
}
|
}
|
121
src/models/Wallet.js
Normal file
121
src/models/Wallet.js
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import db from "../config/database.js";
|
||||||
|
import WalletService from "../utils/walletService.js";
|
||||||
|
|
||||||
|
export default class Wallet {
|
||||||
|
static getBaseWalletType(walletType) {
|
||||||
|
if (walletType.includes('TRC-20')) return 'TRON';
|
||||||
|
if (walletType.includes('ERC-20')) return 'ETH';
|
||||||
|
return walletType;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getArchivedWallets(userId) {
|
||||||
|
const archivedWallets = await db.allAsync(`
|
||||||
|
SELECT * FROM crypto_wallets WHERE user_id = ? AND wallet_type LIKE '%_%'
|
||||||
|
`, [userId]);
|
||||||
|
|
||||||
|
const btcAddress = archivedWallets.find(w => w.wallet_type.startsWith('BTC'))?.address;
|
||||||
|
const ltcAddress = archivedWallets.find(w => w.wallet_type.startsWith('LTC'))?.address;
|
||||||
|
const tronAddress = archivedWallets.find(w => w.wallet_type.startsWith('TRON'))?.address;
|
||||||
|
const ethAddress = archivedWallets.find(w => w.wallet_type.startsWith('ETH'))?.address;
|
||||||
|
|
||||||
|
return {
|
||||||
|
btc: btcAddress,
|
||||||
|
ltc: ltcAddress,
|
||||||
|
tron: tronAddress,
|
||||||
|
eth: ethAddress,
|
||||||
|
wallets: archivedWallets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getActiveWallets(userId) {
|
||||||
|
const activeWallets = await db.allAsync(
|
||||||
|
`SELECT wallet_type, address FROM crypto_wallets WHERE user_id = ? ORDER BY wallet_type`,
|
||||||
|
[userId]
|
||||||
|
)
|
||||||
|
|
||||||
|
const btcAddress = activeWallets.find(w => w.wallet_type === 'BTC')?.address;
|
||||||
|
const ltcAddress = activeWallets.find(w => w.wallet_type === 'LTC')?.address;
|
||||||
|
const tronAddress = activeWallets.find(w => w.wallet_type === 'TRON')?.address;
|
||||||
|
const ethAddress = activeWallets.find(w => w.wallet_type === 'ETH')?.address;
|
||||||
|
|
||||||
|
return {
|
||||||
|
btc: btcAddress,
|
||||||
|
ltc: ltcAddress,
|
||||||
|
tron: tronAddress,
|
||||||
|
eth: ethAddress,
|
||||||
|
wallets: activeWallets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getActiveWalletsBalance(userId) {
|
||||||
|
const activeWallets = await this.getActiveWallets(userId);
|
||||||
|
|
||||||
|
const walletService = new WalletService(
|
||||||
|
activeWallets.btc,
|
||||||
|
activeWallets.ltc,
|
||||||
|
activeWallets.tron,
|
||||||
|
activeWallets.eth,
|
||||||
|
userId,
|
||||||
|
Date.now() - 30 * 24 * 60 * 60 * 1000
|
||||||
|
);
|
||||||
|
|
||||||
|
const balances = await walletService.getAllBalances();
|
||||||
|
|
||||||
|
let totalUsdBalance = 0;
|
||||||
|
|
||||||
|
for (const [type, balance] of Object.entries(balances)) {
|
||||||
|
const baseType = this.getBaseWalletType(type);
|
||||||
|
const wallet = activeWallets.wallets.find(w =>
|
||||||
|
w.wallet_type === baseType ||
|
||||||
|
(type.includes('TRC-20') && w.wallet_type === 'TRON') ||
|
||||||
|
(type.includes('ERC-20') && w.wallet_type === 'ETH')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!wallet) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wallet) {
|
||||||
|
totalUsdBalance += balance.usdValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalUsdBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async getArchivedWalletsBalance(userId) {
|
||||||
|
const archiveWallets = await this.getArchivedWallets(userId);
|
||||||
|
|
||||||
|
const walletService = new WalletService(
|
||||||
|
archiveWallets.btc,
|
||||||
|
archiveWallets.ltc,
|
||||||
|
archiveWallets.tron,
|
||||||
|
archiveWallets.eth,
|
||||||
|
userId,
|
||||||
|
Date.now() - 30 * 24 * 60 * 60 * 1000
|
||||||
|
);
|
||||||
|
|
||||||
|
const balances = await walletService.getAllBalances();
|
||||||
|
|
||||||
|
let totalUsdBalance = 0;
|
||||||
|
|
||||||
|
for (const [type, balance] of Object.entries(balances)) {
|
||||||
|
const baseType = this.getBaseWalletType(type);
|
||||||
|
const wallet = archiveWallets.wallets.find(w =>
|
||||||
|
w.wallet_type === baseType ||
|
||||||
|
(type.includes('TRC-20') && w.wallet_type.startsWith('TRON')) ||
|
||||||
|
(type.includes('ERC-20') && w.wallet_type.startsWith('ETH'))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!wallet) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wallet) {
|
||||||
|
totalUsdBalance += balance.usdValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalUsdBalance;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user