From 3fb2403e85d8e694ec8db60ee1ee5957baf94f46 Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Tue, 24 Mar 2026 09:35:50 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=A2=9E=E5=BC=BA=E4=BB=B7=E6=A0=BC?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E7=A8=B3=E5=AE=9A=E6=80=A7=E5=92=8C=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=BF=BD=E8=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题排查: 1. 缓存命中时验证价格有效性(Price > 0) 2. 外部 API 返回无效价格时拒绝写入缓存 3. 缓存查询层添加详细日志 4. 捕获缓存查询异常并返回 null 改进: - 缓存价格 <= 0 时忽略缓存重新获取 - 外部 API 价格 <= 0 时抛出异常,避免污染缓存 - 详细日志追踪价格获取全流程 --- .../Repositories/MarketDataRepository.cs | 27 ++++++- .../Services/MarketDataService.cs | 74 +++++++++++++------ 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/AssetManager.Data/Repositories/MarketDataRepository.cs b/AssetManager.Data/Repositories/MarketDataRepository.cs index 6a1d20c..2a7feed 100644 --- a/AssetManager.Data/Repositories/MarketDataRepository.cs +++ b/AssetManager.Data/Repositories/MarketDataRepository.cs @@ -25,9 +25,30 @@ public class MarketDataRepository : IMarketDataRepository public async Task GetPriceCacheAsync(string symbol, string assetType) { var cacheKey = GenerateCacheKey(symbol, assetType); - return await _db.Queryable() - .Where(p => p.Id == cacheKey && p.ExpiredAt > DateTime.Now) - .FirstAsync(); + _logger.LogDebug("[缓存查询] CacheKey={CacheKey}, Symbol={Symbol}, AssetType={AssetType}", cacheKey, symbol, assetType); + + try + { + var result = await _db.Queryable() + .Where(p => p.Id == cacheKey && p.ExpiredAt > DateTime.Now) + .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 SavePriceCacheAsync(MarketPriceCache cache) diff --git a/AssetManager.Infrastructure/Services/MarketDataService.cs b/AssetManager.Infrastructure/Services/MarketDataService.cs index cb281e0..3b55804 100755 --- a/AssetManager.Infrastructure/Services/MarketDataService.cs +++ b/AssetManager.Infrastructure/Services/MarketDataService.cs @@ -48,35 +48,54 @@ public class MarketDataService : IMarketDataService public async Task GetPriceAsync(string symbol, string assetType) { var cacheKey = $"{symbol.ToUpper()}_{assetType.ToUpper()}"; - _logger.LogInformation("获取实时价格: {Symbol}, 资产类型: {AssetType}", symbol, assetType); - - // 先查缓存 - var cached = await _marketDataRepo.GetPriceCacheAsync(symbol, assetType); - - if (cached != null) - { - _logger.LogDebug("缓存命中: {Symbol} {AssetType}, 价格: {Price}", symbol, assetType, cached.Price); - return new MarketPriceResponse - { - Symbol = cached.Symbol, - Price = cached.Price, - PreviousClose = cached.PreviousClose ?? 0, - Timestamp = cached.FetchedAt, - AssetType = cached.AssetType - }; - } - - // 使用 GetOrAdd 模式防止并发重复请求 - var priceTask = _pendingPriceRequests.GetOrAdd(cacheKey, _ => FetchPriceFromSourceAsync(symbol, assetType)); + _logger.LogInformation("[价格查询开始] Symbol={Symbol}, AssetType={AssetType}, CacheKey={CacheKey}", symbol, assetType, cacheKey); try { - return await priceTask; + // 先查缓存 + var cached = await _marketDataRepo.GetPriceCacheAsync(symbol, assetType); + + if (cached != null && cached.Price > 0) // ← 验证价格有效 + { + _logger.LogInformation("[缓存命中] Symbol={Symbol}, Price={Price}, ExpiredAt={ExpiredAt}", symbol, cached.Price, cached.ExpiredAt); + return new MarketPriceResponse + { + Symbol = cached.Symbol, + Price = cached.Price, + PreviousClose = cached.PreviousClose ?? 0, + Timestamp = cached.FetchedAt, + AssetType = cached.AssetType + }; + } + + 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 模式防止并发重复请求 + var priceTask = _pendingPriceRequests.GetOrAdd(cacheKey, _ => FetchPriceFromSourceAsync(symbol, assetType)); + + try + { + var result = await priceTask; + _logger.LogInformation("[价格获取成功] Symbol={Symbol}, Price={Price}", symbol, result.Price); + return result; + } + finally + { + // 请求完成后移除(无论成功失败) + _pendingPriceRequests.TryRemove(cacheKey, out _); + } } - finally + catch (Exception ex) { - // 请求完成后移除(无论成功失败) - _pendingPriceRequests.TryRemove(cacheKey, out _); + _logger.LogError(ex, "[价格获取异常] Symbol={Symbol}, AssetType={AssetType}", symbol, assetType); + throw; } } @@ -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);