UNPKG

tryaii-mcp-server

Version:

TryAII MCP Server - 15+ AI models with comparison, cost tracking, and collective intelligence

204 lines 7.94 kB
import { logger } from '../utils/logger.js'; import { databaseService } from './database.js'; import { UserSchema, TransactionSchema } from '../models/existing/ExistingModels.js'; export class BalanceService { static instance; UserModel; TransactionModel; constructor() { } static getInstance() { if (!BalanceService.instance) { BalanceService.instance = new BalanceService(); } return BalanceService.instance; } async initialize() { try { const mainConnection = databaseService.getMainConnection(); this.UserModel = mainConnection.model('User', UserSchema); this.TransactionModel = mainConnection.model('Transaction', TransactionSchema); logger.info('Balance Service initialized successfully'); } catch (error) { logger.error('Failed to initialize Balance Service', { error }); throw error; } } async deductBalance(userId, amount, description, metadata) { const session = await databaseService.getMainConnection().startSession(); try { await session.withTransaction(async () => { // Get current user with session for atomicity const user = await this.UserModel.findById(userId).session(session); if (!user) { throw new Error(`User not found: ${userId}`); } const currentBalance = user.balance?.currentAmount || 0; if (currentBalance < amount) { throw new Error(`Insufficient balance. Required: ${amount}, Available: ${currentBalance}`); } const newBalance = parseFloat((currentBalance - amount).toFixed(6)); // Update user balance atomically await this.UserModel.findByIdAndUpdate(userId, { $set: { 'balance.currentAmount': newBalance, 'balance.lastUpdated': new Date() } }, { session }); // Create transaction record const transaction = new this.TransactionModel({ userId, amount: -amount, balanceAfter: newBalance, description, type: 'usage', status: 'completed', metadata: { ipAddress: metadata?.ipAddress || '', userAgent: metadata?.userAgent || '', requestId: metadata?.requestId || '' }, // 🆕 Enhanced token and cost tracking tokenUsage: metadata?.tokenUsage || { totalTokens: 0, thinkingTokens: 0, reasoningModelsUsed: 0 }, costBreakdown: metadata?.costBreakdown || { inputCost: 0, outputCost: 0, thinkingCost: 0 } }); await transaction.save({ session }); logger.info('Balance deducted successfully', { userId, amount, balanceAfter: newBalance, transactionId: transaction._id, description }); return { success: true, transactionId: transaction._id.toString(), balanceAfter: newBalance }; }); return { success: true, transactionId: 'success', // Will be overwritten in transaction balanceAfter: 0 // Will be overwritten in transaction }; } catch (error) { logger.error('Failed to deduct balance', { userId, amount, error: error.message }); return { success: false, errorMessage: error.message }; } finally { await session.endSession(); } } async addBalance(userId, amount, description, type = 'deposit', paymentReference, metadata) { const session = await databaseService.getMainConnection().startSession(); try { let result = { success: false }; await session.withTransaction(async () => { // Get current user with session for atomicity const user = await this.UserModel.findById(userId).session(session); if (!user) { throw new Error(`User not found: ${userId}`); } const currentBalance = user.balance?.currentAmount || 0; const newBalance = parseFloat((currentBalance + amount).toFixed(6)); // Update user balance atomically await this.UserModel.findByIdAndUpdate(userId, { $set: { 'balance.currentAmount': newBalance, 'balance.lastUpdated': new Date() } }, { session }); // Create transaction record const transaction = new this.TransactionModel({ userId, amount, balanceAfter: newBalance, description, type, status: 'completed', paymentReference, metadata: { ipAddress: metadata?.ipAddress || '', userAgent: metadata?.userAgent || '', adminId: metadata?.adminId || '' } }); await transaction.save({ session }); logger.info('Balance added successfully', { userId, amount, balanceAfter: newBalance, transactionId: transaction._id, type, description }); result = { success: true, transactionId: transaction._id.toString(), balanceAfter: newBalance }; }); return result; } catch (error) { logger.error('Failed to add balance', { userId, amount, error: error.message }); return { success: false, errorMessage: error.message }; } finally { await session.endSession(); } } async getCurrentBalance(userId) { try { const user = await this.UserModel.findById(userId).lean(); return user?.balance?.currentAmount || 0; } catch (error) { logger.error('Failed to get current balance', { userId, error }); return 0; } } async checkSufficientBalance(userId, requiredAmount) { const currentBalance = await this.getCurrentBalance(userId); return currentBalance >= requiredAmount; } // Get recent transactions for a user async getRecentTransactions(userId, limit = 10) { try { return await this.TransactionModel .find({ userId }) .sort({ createdAt: -1 }) .limit(limit) .lean(); } catch (error) { logger.error('Failed to get recent transactions', { userId, error }); return []; } } } export const balanceService = BalanceService.getInstance(); //# sourceMappingURL=BalanceService.js.map