tryaii-mcp-server
Version:
TryAII MCP Server - 15+ AI models with comparison, cost tracking, and collective intelligence
204 lines • 7.94 kB
JavaScript
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