fix: 增强价格获取稳定性和日志追踪

问题排查:
1. 缓存命中时验证价格有效性(Price > 0)
2. 外部 API 返回无效价格时拒绝写入缓存
3. 缓存查询层添加详细日志
4. 捕获缓存查询异常并返回 null

改进:
- 缓存价格 <= 0 时忽略缓存重新获取
- 外部 API 价格 <= 0 时抛出异常,避免污染缓存
- 详细日志追踪价格获取全流程
This commit is contained in:
OpenClaw Agent 2026-03-24 09:35:50 +00:00
parent 3768f6e747
commit 3fb2403e85
2 changed files with 74 additions and 27 deletions

View File

@ -25,9 +25,30 @@ public class MarketDataRepository : IMarketDataRepository
public async Task<MarketPriceCache?> GetPriceCacheAsync(string symbol, string assetType) public async Task<MarketPriceCache?> GetPriceCacheAsync(string symbol, string assetType)
{ {
var cacheKey = GenerateCacheKey(symbol, assetType); var cacheKey = GenerateCacheKey(symbol, assetType);
return await _db.Queryable<MarketPriceCache>() _logger.LogDebug("[缓存查询] CacheKey={CacheKey}, Symbol={Symbol}, AssetType={AssetType}", cacheKey, symbol, assetType);
try
{
var result = await _db.Queryable<MarketPriceCache>()
.Where(p => p.Id == cacheKey && p.ExpiredAt > DateTime.Now) .Where(p => p.Id == cacheKey && p.ExpiredAt > DateTime.Now)
.FirstAsync(); .FirstAsync();
if (result != null)
{
_logger.LogDebug("[缓存查询成功] CacheKey={CacheKey}, Price={Price}, ExpiredAt={ExpiredAt}", cacheKey, result.Price, result.ExpiredAt);
}
else
{
_logger.LogDebug("[缓存查询无结果] CacheKey={CacheKey}", cacheKey);
}
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, "[缓存查询异常] CacheKey={CacheKey}", cacheKey);
return null;
}
} }
public async Task<bool> SavePriceCacheAsync(MarketPriceCache cache) public async Task<bool> SavePriceCacheAsync(MarketPriceCache cache)

View File

@ -48,14 +48,16 @@ public class MarketDataService : IMarketDataService
public async Task<MarketPriceResponse> GetPriceAsync(string symbol, string assetType) public async Task<MarketPriceResponse> GetPriceAsync(string symbol, string assetType)
{ {
var cacheKey = $"{symbol.ToUpper()}_{assetType.ToUpper()}"; var cacheKey = $"{symbol.ToUpper()}_{assetType.ToUpper()}";
_logger.LogInformation("获取实时价格: {Symbol}, 资产类型: {AssetType}", symbol, assetType); _logger.LogInformation("[价格查询开始] Symbol={Symbol}, AssetType={AssetType}, CacheKey={CacheKey}", symbol, assetType, cacheKey);
try
{
// 先查缓存 // 先查缓存
var cached = await _marketDataRepo.GetPriceCacheAsync(symbol, assetType); var cached = await _marketDataRepo.GetPriceCacheAsync(symbol, assetType);
if (cached != null) if (cached != null && cached.Price > 0) // ← 验证价格有效
{ {
_logger.LogDebug("缓存命中: {Symbol} {AssetType}, 价格: {Price}", symbol, assetType, cached.Price); _logger.LogInformation("[缓存命中] Symbol={Symbol}, Price={Price}, ExpiredAt={ExpiredAt}", symbol, cached.Price, cached.ExpiredAt);
return new MarketPriceResponse return new MarketPriceResponse
{ {
Symbol = cached.Symbol, Symbol = cached.Symbol,
@ -66,12 +68,23 @@ public class MarketDataService : IMarketDataService
}; };
} }
if (cached != null && cached.Price <= 0)
{
_logger.LogWarning("[缓存命中但价格无效] Symbol={Symbol}, Price={Price},忽略缓存重新获取", symbol, cached.Price);
}
else
{
_logger.LogWarning("[缓存未命中] Symbol={Symbol}, AssetType={AssetType},需要从数据源获取", symbol, assetType);
}
// 使用 GetOrAdd 模式防止并发重复请求 // 使用 GetOrAdd 模式防止并发重复请求
var priceTask = _pendingPriceRequests.GetOrAdd(cacheKey, _ => FetchPriceFromSourceAsync(symbol, assetType)); var priceTask = _pendingPriceRequests.GetOrAdd(cacheKey, _ => FetchPriceFromSourceAsync(symbol, assetType));
try try
{ {
return await priceTask; var result = await priceTask;
_logger.LogInformation("[价格获取成功] Symbol={Symbol}, Price={Price}", symbol, result.Price);
return result;
} }
finally finally
{ {
@ -79,6 +92,12 @@ public class MarketDataService : IMarketDataService
_pendingPriceRequests.TryRemove(cacheKey, out _); _pendingPriceRequests.TryRemove(cacheKey, out _);
} }
} }
catch (Exception ex)
{
_logger.LogError(ex, "[价格获取异常] Symbol={Symbol}, AssetType={AssetType}", symbol, assetType);
throw;
}
}
/// <summary> /// <summary>
/// 从数据源获取价格(内部方法) /// 从数据源获取价格(内部方法)
@ -120,6 +139,13 @@ public class MarketDataService : IMarketDataService
} }
} }
// 验证价格有效性
if (response.Price <= 0)
{
_logger.LogError("[价格无效] Symbol={Symbol}, Price={Price}, Source={Source},不写入缓存", symbol, response.Price, source);
throw new InvalidOperationException($"获取到的价格无效: {symbol} = {response.Price}");
}
// 写入缓存 // 写入缓存
await SavePriceCacheAsync(symbol, assetType, response, source); await SavePriceCacheAsync(symbol, assetType, response, source);