using System.Security.Cryptography; using System.Text; using AssetManager.Data; using Microsoft.Extensions.Logging; using SqlSugar; namespace AssetManager.Data.Repositories; /// /// 市场数据仓储实现 /// public class MarketDataRepository : IMarketDataRepository { private readonly ISqlSugarClient _db; private readonly ILogger _logger; public MarketDataRepository(ISqlSugarClient db, ILogger logger) { _db = db; _logger = logger; } // ===== 价格缓存 ===== 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(); } public async Task SavePriceCacheAsync(MarketPriceCache cache) { cache.Id = GenerateCacheKey(cache.Symbol, cache.AssetType); var result = await _db.Storageable(cache).ExecuteCommandAsync(); return result > 0; } // ===== K线缓存 ===== public async Task> GetKlineCacheAsync(string symbol, string assetType, string timeframe, int limit) { return await _db.Queryable() .Where(k => k.Symbol == symbol.ToUpper() && k.AssetType == assetType.ToUpper() && k.Timeframe == timeframe.ToUpper()) .OrderByDescending(k => k.Timestamp) .Take(limit) .ToListAsync(); } public async Task SaveKlineCacheBatchAsync(List cacheList) { if (cacheList == null || cacheList.Count == 0) return false; // 为每条记录生成唯一ID foreach (var cache in cacheList) { cache.Id = GenerateKlineCacheKey(cache.Symbol, cache.AssetType, cache.Timeframe, cache.Timestamp); } var result = await _db.Storageable(cacheList) .WhereColumns(it => new { it.Id }) .ExecuteCommandAsync(); return result > 0; } // ===== Tiingo Ticker ===== public async Task GetTiingoTickerAsync(string symbol) { return await _db.Queryable() .Where(t => t.Ticker == symbol.ToUpper()) .FirstAsync(); } public async Task SaveTiingoTickerAsync(TiingoTicker ticker) { var result = await _db.Storageable(ticker) .WhereColumns(it => new { it.Ticker }) .ExecuteCommandAsync(); return result > 0; } public async Task> SearchTiingoTickersAsync(string keyword, int limit) { return await _db.Queryable() .Where(t => t.Ticker.Contains(keyword.ToUpper()) || (t.Name != null && t.Name.Contains(keyword))) .Take(limit) .ToListAsync(); } // ===== 私有辅助方法 ===== private string GenerateCacheKey(string symbol, string assetType) { return GenerateMd5Hash($"{symbol.ToUpper()}_{assetType.ToUpper()}"); } private string GenerateKlineCacheKey(string symbol, string assetType, string timeframe, DateTime timestamp) { return GenerateMd5Hash($"{symbol.ToUpper()}_{assetType.ToUpper()}_{timeframe.ToUpper()}_{timestamp:yyyyMMddHHmmss}"); } private string GenerateMd5Hash(string input) { using var md5 = MD5.Create(); var inputBytes = Encoding.UTF8.GetBytes(input); var hashBytes = md5.ComputeHash(inputBytes); var sb = new StringBuilder(); foreach (var b in hashBytes) { sb.Append(b.ToString("x2")); } return sb.ToString(); } }