Files
vf_react/src/services/creditSystemService.js
2025-11-23 18:11:48 +08:00

493 lines
11 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 积分系统服务
* 管理用户积分账户、交易、奖励等
*/
// ==================== 常量配置 ====================
export const CREDIT_CONFIG = {
INITIAL_BALANCE: 10000, // 初始积分
MIN_BALANCE: 100, // 最低保留余额(破产保护)
MAX_SINGLE_BET: 1000, // 单次下注上限
DAILY_BONUS: 100, // 每日签到奖励
CREATE_TOPIC_COST: 100, // 创建话题费用
};
// 积分账户存储(生产环境应使用数据库)
const userAccounts = new Map();
// 交易记录存储
const transactions = [];
// ==================== 账户管理 ====================
/**
* 获取用户账户
* @param {string} userId - 用户ID
* @returns {Object} 用户账户信息
*/
export const getUserAccount = (userId) => {
if (!userAccounts.has(userId)) {
// 首次访问,创建新账户
const newAccount = {
user_id: userId,
balance: CREDIT_CONFIG.INITIAL_BALANCE,
frozen: 0,
total: CREDIT_CONFIG.INITIAL_BALANCE,
total_earned: CREDIT_CONFIG.INITIAL_BALANCE,
total_spent: 0,
total_profit: 0,
active_positions: [],
stats: {
total_topics: 0,
win_count: 0,
loss_count: 0,
win_rate: 0,
best_profit: 0,
},
last_daily_bonus: null,
};
userAccounts.set(userId, newAccount);
}
return userAccounts.get(userId);
};
/**
* 更新用户账户
* @param {string} userId - 用户ID
* @param {Object} updates - 更新内容
*/
export const updateUserAccount = (userId, updates) => {
const account = getUserAccount(userId);
const updated = { ...account, ...updates };
userAccounts.set(userId, updated);
return updated;
};
/**
* 获取用户积分余额
* @param {string} userId - 用户ID
* @returns {number} 可用余额
*/
export const getBalance = (userId) => {
const account = getUserAccount(userId);
return account.balance;
};
/**
* 检查用户是否能支付
* @param {string} userId - 用户ID
* @param {number} amount - 金额
* @returns {boolean} 是否能支付
*/
export const canAfford = (userId, amount) => {
const account = getUserAccount(userId);
const afterBalance = account.balance - amount;
// 必须保留最低余额
return afterBalance >= CREDIT_CONFIG.MIN_BALANCE;
};
// ==================== 积分操作 ====================
/**
* 增加积分
* @param {string} userId - 用户ID
* @param {number} amount - 金额
* @param {string} reason - 原因
*/
export const addCredits = (userId, amount, reason = '系统增加') => {
const account = getUserAccount(userId);
const updated = {
balance: account.balance + amount,
total: account.total + amount,
total_earned: account.total_earned + amount,
};
updateUserAccount(userId, updated);
// 记录交易
logTransaction({
user_id: userId,
type: 'earn',
amount,
reason,
balance_after: updated.balance,
});
return updated;
};
/**
* 扣除积分
* @param {string} userId - 用户ID
* @param {number} amount - 金额
* @param {string} reason - 原因
* @throws {Error} 如果余额不足
*/
export const deductCredits = (userId, amount, reason = '系统扣除') => {
if (!canAfford(userId, amount)) {
throw new Error(`积分不足,需要${amount}积分,但只有${getBalance(userId)}积分`);
}
const account = getUserAccount(userId);
const updated = {
balance: account.balance - amount,
total_spent: account.total_spent + amount,
};
updateUserAccount(userId, updated);
// 记录交易
logTransaction({
user_id: userId,
type: 'spend',
amount: -amount,
reason,
balance_after: updated.balance,
});
return updated;
};
/**
* 冻结积分(席位占用)
* @param {string} userId - 用户ID
* @param {number} amount - 金额
*/
export const freezeCredits = (userId, amount) => {
const account = getUserAccount(userId);
if (account.balance < amount) {
throw new Error('可用余额不足');
}
const updated = {
balance: account.balance - amount,
frozen: account.frozen + amount,
};
updateUserAccount(userId, updated);
return updated;
};
/**
* 解冻积分
* @param {string} userId - 用户ID
* @param {number} amount - 金额
*/
export const unfreezeCredits = (userId, amount) => {
const account = getUserAccount(userId);
const updated = {
balance: account.balance + amount,
frozen: account.frozen - amount,
};
updateUserAccount(userId, updated);
return updated;
};
// ==================== 每日奖励 ====================
/**
* 领取每日签到奖励
* @param {string} userId - 用户ID
* @returns {Object} 奖励信息
*/
export const claimDailyBonus = (userId) => {
const account = getUserAccount(userId);
const today = new Date().toDateString();
// 检查是否已领取
if (account.last_daily_bonus === today) {
return {
success: false,
message: '今日已领取',
};
}
// 发放奖励
addCredits(userId, CREDIT_CONFIG.DAILY_BONUS, '每日签到');
// 更新领取时间
updateUserAccount(userId, { last_daily_bonus: today });
return {
success: true,
amount: CREDIT_CONFIG.DAILY_BONUS,
message: `获得${CREDIT_CONFIG.DAILY_BONUS}积分`,
};
};
/**
* 检查今天是否已签到
* @param {string} userId - 用户ID
* @returns {boolean}
*/
export const hasClaimedToday = (userId) => {
const account = getUserAccount(userId);
const today = new Date().toDateString();
return account.last_daily_bonus === today;
};
// ==================== 持仓管理 ====================
/**
* 添加持仓
* @param {string} userId - 用户ID
* @param {Object} position - 持仓信息
*/
export const addPosition = (userId, position) => {
const account = getUserAccount(userId);
const updated = {
active_positions: [...account.active_positions, position],
stats: {
...account.stats,
total_topics: account.stats.total_topics + 1,
},
};
updateUserAccount(userId, updated);
return updated;
};
/**
* 移除持仓
* @param {string} userId - 用户ID
* @param {string} positionId - 持仓ID
*/
export const removePosition = (userId, positionId) => {
const account = getUserAccount(userId);
const updated = {
active_positions: account.active_positions.filter((p) => p.id !== positionId),
};
updateUserAccount(userId, updated);
return updated;
};
/**
* 更新持仓
* @param {string} userId - 用户ID
* @param {string} positionId - 持仓ID
* @param {Object} updates - 更新内容
*/
export const updatePosition = (userId, positionId, updates) => {
const account = getUserAccount(userId);
const updated = {
active_positions: account.active_positions.map((p) =>
p.id === positionId ? { ...p, ...updates } : p
),
};
updateUserAccount(userId, updated);
return updated;
};
/**
* 获取用户持仓
* @param {string} userId - 用户ID
* @param {string} topicId - 话题ID可选
* @returns {Array} 持仓列表
*/
export const getUserPositions = (userId, topicId = null) => {
const account = getUserAccount(userId);
if (topicId) {
return account.active_positions.filter((p) => p.topic_id === topicId);
}
return account.active_positions;
};
// ==================== 统计更新 ====================
/**
* 记录胜利
* @param {string} userId - 用户ID
* @param {number} profit - 盈利金额
*/
export const recordWin = (userId, profit) => {
const account = getUserAccount(userId);
const newWinCount = account.stats.win_count + 1;
const totalGames = newWinCount + account.stats.loss_count;
const winRate = (newWinCount / totalGames) * 100;
const updated = {
total_profit: account.total_profit + profit,
stats: {
...account.stats,
win_count: newWinCount,
win_rate: winRate,
best_profit: Math.max(account.stats.best_profit, profit),
},
};
updateUserAccount(userId, updated);
return updated;
};
/**
* 记录失败
* @param {string} userId - 用户ID
* @param {number} loss - 损失金额
*/
export const recordLoss = (userId, loss) => {
const account = getUserAccount(userId);
const newLossCount = account.stats.loss_count + 1;
const totalGames = account.stats.win_count + newLossCount;
const winRate = (account.stats.win_count / totalGames) * 100;
const updated = {
total_profit: account.total_profit - loss,
stats: {
...account.stats,
loss_count: newLossCount,
win_rate: winRate,
},
};
updateUserAccount(userId, updated);
return updated;
};
// ==================== 排行榜 ====================
/**
* 获取积分排行榜
* @param {number} limit - 返回数量
* @returns {Array} 排行榜数据
*/
export const getLeaderboard = (limit = 100) => {
const accounts = Array.from(userAccounts.values());
return accounts
.sort((a, b) => b.total - a.total)
.slice(0, limit)
.map((account, index) => ({
rank: index + 1,
user_id: account.user_id,
total: account.total,
total_profit: account.total_profit,
win_rate: account.stats.win_rate,
}));
};
/**
* 获取用户排名
* @param {string} userId - 用户ID
* @returns {number} 排名
*/
export const getUserRank = (userId) => {
const leaderboard = getLeaderboard(1000);
const index = leaderboard.findIndex((item) => item.user_id === userId);
return index >= 0 ? index + 1 : -1;
};
// ==================== 交易记录 ====================
/**
* 记录交易
* @param {Object} transaction - 交易信息
*/
const logTransaction = (transaction) => {
const record = {
id: `tx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
timestamp: new Date().toISOString(),
...transaction,
};
transactions.push(record);
return record;
};
/**
* 获取用户交易记录
* @param {string} userId - 用户ID
* @param {number} limit - 返回数量
* @returns {Array} 交易记录
*/
export const getUserTransactions = (userId, limit = 50) => {
return transactions
.filter((tx) => tx.user_id === userId)
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
.slice(0, limit);
};
// ==================== 批量操作 ====================
/**
* 批量发放积分(如活动奖励)
* @param {Array} recipients - [{user_id, amount, reason}]
*/
export const batchAddCredits = (recipients) => {
const results = recipients.map(({ user_id, amount, reason }) => {
try {
return {
user_id,
success: true,
account: addCredits(user_id, amount, reason),
};
} catch (error) {
return {
user_id,
success: false,
error: error.message,
};
}
});
return results;
};
// ==================== 导出所有功能 ====================
export default {
CREDIT_CONFIG,
// 账户管理
getUserAccount,
updateUserAccount,
getBalance,
canAfford,
// 积分操作
addCredits,
deductCredits,
freezeCredits,
unfreezeCredits,
// 每日奖励
claimDailyBonus,
hasClaimedToday,
// 持仓管理
addPosition,
removePosition,
updatePosition,
getUserPositions,
// 统计更新
recordWin,
recordLoss,
// 排行榜
getLeaderboard,
getUserRank,
// 交易记录
getUserTransactions,
// 批量操作
batchAddCredits,
};