feat: 实现发送消息前余额检测

- creditsService.ts: 新增余额缓存和检测服务
- apiClient.ts: 新增 getCreditBalance() API 调用
- dialogService.ts: SSE credit_update 事件更新余额缓存
- messageHandler.ts: 发送消息前检测余额,低于5点阻止发送
This commit is contained in:
XiaoFeng
2026-01-10 21:45:41 +08:00
parent 52e4522ed0
commit bdc55c727a
4 changed files with 165 additions and 0 deletions

View File

@ -0,0 +1,121 @@
/**
* 资源点余额管理服务
* 负责缓存余额、主动查询、发送前检测
*/
import { getCreditBalance } from './apiClient';
import { getCachedUserInfo } from './userService';
/** 低余额阈值 */
const LOW_CREDIT_THRESHOLD = 5;
/** 缓存的余额 */
let cachedBalance: number | null = null;
/** 最后更新时间 */
let lastUpdateTime: number = 0;
/** 缓存有效期5分钟 */
const CACHE_TTL_MS = 5 * 60 * 1000;
/**
* 更新缓存的余额(从 SSE credit_update 事件调用)
*/
export function updateCachedBalance(balance: number): void {
cachedBalance = balance;
lastUpdateTime = Date.now();
console.log('[CreditsService] 余额已更新:', balance);
}
/**
* 获取缓存的余额
*/
export function getCachedBalance(): number | null {
return cachedBalance;
}
/**
* 检查缓存是否有效
*/
function isCacheValid(): boolean {
if (cachedBalance === null) return false;
return Date.now() - lastUpdateTime < CACHE_TTL_MS;
}
/**
* 主动查询余额
*/
export async function fetchBalance(): Promise<number | null> {
const userInfo = getCachedUserInfo();
if (!userInfo?.userId) {
console.warn('[CreditsService] 无法查询余额:未登录');
return null;
}
try {
const response = await getCreditBalance(userInfo.userId);
if (response.success && response.balance !== undefined) {
updateCachedBalance(response.balance);
return response.balance;
} else {
console.warn('[CreditsService] 查询余额失败:', response.error);
return null;
}
} catch (error) {
console.error('[CreditsService] 查询余额异常:', error);
return null;
}
}
/**
* 获取当前余额(优先使用缓存,过期则主动查询)
*/
export async function getBalance(): Promise<number | null> {
if (isCacheValid()) {
return cachedBalance;
}
return await fetchBalance();
}
/**
* 检查余额是否足够发送消息
* @returns { allowed: boolean, balance: number | null, message?: string }
*/
export async function checkBalanceBeforeSend(): Promise<{
allowed: boolean;
balance: number | null;
message?: string;
}> {
const userInfo = getCachedUserInfo();
if (!userInfo) {
// 未登录,允许发送(后端会处理)
return { allowed: true, balance: null };
}
const balance = await getBalance();
if (balance === null) {
// 无法获取余额,允许发送(后端会处理)
console.warn('[CreditsService] 无法获取余额,允许发送');
return { allowed: true, balance: null };
}
if (balance < LOW_CREDIT_THRESHOLD) {
return {
allowed: false,
balance,
message: `资源点余额不足!当前余额 ${balance.toFixed(2)} 点,低于最低要求 ${LOW_CREDIT_THRESHOLD} 点。请充值后再试。`
};
}
return { allowed: true, balance };
}
/**
* 清除缓存(登出时调用)
*/
export function clearBalanceCache(): void {
cachedBalance = null;
lastUpdateTime = 0;
console.log('[CreditsService] 余额缓存已清除');
}