From 64c1fe60e7b17591653903be4932cc499d6b48ef Mon Sep 17 00:00:00 2001 From: OpenClaw Agent Date: Wed, 25 Mar 2026 05:31:53 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E9=87=91=E8=9E=8D=E8=AE=A1=E7=AE=97=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. CreatePortfolioAsync: 初始建仓交易保存汇率信息 - 设置 ExchangeRate 和 TotalAmountBase 字段 - 支持跨币种初始建仓 2. ExchangeRateService: 增强 Mock 汇率降级 - 扩展支持 EUR、GBP、JPY - 未知货币对记录 Error 级别日志 3. PositionItem: 增加 Shares 属性 - 保留完整精度(解决 Amount int 截断问题) --- .../Services/ExchangeRateService.cs | 28 +++++++++++++++++-- AssetManager.Models/DTOs/PortfolioDTO.cs | 3 +- AssetManager.Services/PortfolioService.cs | 27 +++++++++++++++--- 3 files changed, 51 insertions(+), 7 deletions(-) diff --git a/AssetManager.Infrastructure/Services/ExchangeRateService.cs b/AssetManager.Infrastructure/Services/ExchangeRateService.cs index 80d7f61..21e32a2 100755 --- a/AssetManager.Infrastructure/Services/ExchangeRateService.cs +++ b/AssetManager.Infrastructure/Services/ExchangeRateService.cs @@ -116,11 +116,35 @@ public class ExchangeRateService : IExchangeRateService { "CNY-HKD", 1.09m }, { "HKD-CNY", 0.92m }, { "USD-HKD", 7.75m }, - { "HKD-USD", 0.13m } + { "HKD-USD", 0.13m }, + { "EUR-USD", 1.08m }, + { "USD-EUR", 0.93m }, + { "EUR-CNY", 7.70m }, + { "CNY-EUR", 0.13m }, + { "GBP-USD", 1.27m }, + { "USD-GBP", 0.79m }, + { "GBP-CNY", 9.00m }, + { "CNY-GBP", 0.11m }, + { "JPY-USD", 0.0067m }, + { "USD-JPY", 149.50m }, + { "JPY-CNY", 0.048m }, + { "CNY-JPY", 20.90m } }; string key = $"{fromCurrency.ToUpper()}-{toCurrency.ToUpper()}"; - return Task.FromResult(mockRates.TryGetValue(key, out var rate) ? rate : 1.00m); + + if (mockRates.TryGetValue(key, out var rate)) + { + _logger.LogWarning("Mock汇率命中: {Key} = {Rate},计算结果可能不准确", key, rate); + return Task.FromResult(rate); + } + + // 未知货币对,记录严重警告 + _logger.LogError("未知货币对,无法提供Mock汇率: {FromCurrency} -> {ToCurrency},返回1.0可能导致计算错误", + fromCurrency, toCurrency); + + // 返回1.0但记录严重警告,调用方应该检查日志 + return Task.FromResult(1.00m); } } diff --git a/AssetManager.Models/DTOs/PortfolioDTO.cs b/AssetManager.Models/DTOs/PortfolioDTO.cs index 316c7ff..a456cb0 100755 --- a/AssetManager.Models/DTOs/PortfolioDTO.cs +++ b/AssetManager.Models/DTOs/PortfolioDTO.cs @@ -90,7 +90,8 @@ public class PositionItem public string? StockCode { get; set; } public string? StockName { get; set; } public string? Symbol { get; set; } - public int Amount { get; set; } + public int Amount { get; set; } // 已弃用,保留兼容性 + public double Shares { get; set; } // 完整精度的持仓数量 public double AveragePrice { get; set; } public double CurrentPrice { get; set; } public double TotalValue { get; set; } diff --git a/AssetManager.Services/PortfolioService.cs b/AssetManager.Services/PortfolioService.cs index 3c2bb22..ddb7dec 100755 --- a/AssetManager.Services/PortfolioService.cs +++ b/AssetManager.Services/PortfolioService.cs @@ -138,7 +138,23 @@ public class PortfolioService : IPortfolioService _db.Insertable(position).ExecuteCommand(); - // 创建交易记录 + // 创建交易记录(保存汇率信息) + decimal totalAmount = (decimal)(stock.Price * stock.Amount); + decimal? exchangeRate = null; + decimal? totalAmountBase = null; + + // 如果持仓币种与组合币种不同,需要保存汇率 + if (!string.IsNullOrEmpty(stock.Currency) && !stock.Currency.Equals(request.Currency, StringComparison.OrdinalIgnoreCase)) + { + exchangeRate = await _exchangeRateService.GetExchangeRateAsync(stock.Currency, request.Currency); + totalAmountBase = totalAmount * exchangeRate.Value; + } + else + { + exchangeRate = 1.0m; + totalAmountBase = totalAmount; + } + var transaction = new Transaction { Id = "trans-" + Guid.NewGuid().ToString().Substring(0, 8), @@ -149,8 +165,10 @@ public class PortfolioService : IPortfolioService Title = "初始建仓", Amount = (decimal)stock.Amount, Price = (decimal)stock.Price, - TotalAmount = (decimal)(stock.Price * stock.Amount), - Currency = request.Currency, + TotalAmount = totalAmount, + Currency = stock.Currency ?? request.Currency, + ExchangeRate = exchangeRate, + TotalAmountBase = totalAmountBase, Status = "completed", Remark = "初始建仓", TransactionTime = buyTime, @@ -524,7 +542,8 @@ public class PortfolioService : IPortfolioService StockCode = pos.StockCode, StockName = pos.StockName, Symbol = pos.StockCode, - Amount = (int)pos.Shares, + Amount = (int)pos.Shares, // 注意:此处精度丢失,仅用于显示 + Shares = (double)pos.Shares, // 新增:保留完整精度 AveragePrice = (double)pos.AvgPrice, CurrentPrice = (double)CurrentPrice, TotalValue = (double)positionValueInTarget,