diff --git a/AssetManager.API/Controllers/PortfolioController.cs b/AssetManager.API/Controllers/PortfolioController.cs index c458a20..513e8e9 100755 --- a/AssetManager.API/Controllers/PortfolioController.cs +++ b/AssetManager.API/Controllers/PortfolioController.cs @@ -45,7 +45,7 @@ public class PortfolioController : ControllerBase var response = await _portfolioFacade.CreatePortfolioWithValidationAsync(request, userId); return Ok(new ApiResponse { - code = StatusCodes.Success, + code = Models.StatusCodes.Success, data = response, message = "创建成功" }); @@ -54,7 +54,7 @@ public class PortfolioController : ControllerBase { return BadRequest(new ApiResponse { - code = StatusCodes.BadRequest, + code = Models.StatusCodes.BadRequest, data = null, message = ex.Message }); @@ -72,7 +72,7 @@ public class PortfolioController : ControllerBase return Ok(new ApiResponse> { - code = StatusCodes.Success, + code = Models.StatusCodes.Success, data = portfolios, message = "success" }); @@ -91,7 +91,7 @@ public class PortfolioController : ControllerBase var portfolio = await _portfolioFacade.GetPortfolioDetailAsync(id, userId); return Ok(new ApiResponse { - code = StatusCodes.Success, + code = Models.StatusCodes.Success, data = portfolio, message = "success" }); @@ -100,7 +100,7 @@ public class PortfolioController : ControllerBase { return NotFound(new ApiResponse { - code = StatusCodes.NotFound, + code = Models.StatusCodes.NotFound, data = null, message = "组合不存在" }); @@ -120,7 +120,7 @@ public class PortfolioController : ControllerBase { return NotFound(new ApiResponse { - code = StatusCodes.NotFound, + code = Models.StatusCodes.NotFound, data = null, message = "组合不存在" }); @@ -128,7 +128,7 @@ public class PortfolioController : ControllerBase return Ok(new ApiResponse { - code = StatusCodes.Success, + code = Models.StatusCodes.Success, data = null, message = "删除成功" }); @@ -148,9 +148,9 @@ public class PortfolioController : ControllerBase var request = new NavHistoryRequest { - startDate = startDate, - endDate = endDate, - interval = interval ?? "daily" + StartDate = startDate, + EndDate = endDate, + Interval = interval ?? "daily" }; try @@ -158,7 +158,7 @@ public class PortfolioController : ControllerBase var response = await _portfolioFacade.GetNavHistoryAsync(id, request, userId); return Ok(new ApiResponse { - code = StatusCodes.Success, + code = Models.StatusCodes.Success, data = response, message = "success" }); @@ -167,7 +167,7 @@ public class PortfolioController : ControllerBase { return NotFound(new ApiResponse { - code = StatusCodes.NotFound, + code = Models.StatusCodes.NotFound, data = null, message = "组合不存在" }); @@ -189,7 +189,7 @@ public class PortfolioController : ControllerBase var response = await _portfolioFacade.BackfillNavHistoryAsync(id, userId, force); return Ok(new ApiResponse { - code = StatusCodes.Success, + code = Models.StatusCodes.Success, data = response, message = "success" }); @@ -198,7 +198,7 @@ public class PortfolioController : ControllerBase { return NotFound(new ApiResponse { - code = StatusCodes.NotFound, + code = Models.StatusCodes.NotFound, data = null, message = "组合不存在" }); @@ -220,7 +220,7 @@ public class PortfolioController : ControllerBase var transaction = await _portfolioFacade.CreateTransactionAsync(id, request, userId); return Ok(new ApiResponse { - code = StatusCodes.Success, + code = Models.StatusCodes.Success, data = transaction, message = "交易创建成功" }); @@ -229,7 +229,7 @@ public class PortfolioController : ControllerBase { return NotFound(new ApiResponse { - code = StatusCodes.NotFound, + code = Models.StatusCodes.NotFound, data = null, message = "组合不存在" }); @@ -249,20 +249,20 @@ public class PortfolioController : ControllerBase var request = new GetTransactionsRequest { - portfolioId = id, - limit = limit, - offset = offset + PortfolioId = id, + Limit = limit, + Offset = offset }; var transactions = await _portfolioFacade.GetTransactionsAsync(id, request, userId); return Ok(new ApiResponse { - code = StatusCodes.Success, + code = Models.StatusCodes.Success, data = new GetTransactionsResponse { - items = transactions, - total = transactions.Count + Items = transactions, + Total = transactions.Count }, message = "success" }); @@ -281,7 +281,7 @@ public class PortfolioController : ControllerBase var signal = await _portfolioFacade.GetStrategySignalAsync(id, userId); return Ok(new ApiResponse { - code = StatusCodes.Success, + code = Models.StatusCodes.Success, data = signal, message = "success" }); @@ -290,7 +290,7 @@ public class PortfolioController : ControllerBase { return NotFound(new ApiResponse { - code = StatusCodes.NotFound, + code = Models.StatusCodes.NotFound, data = null, message = "组合不存在" }); diff --git a/AssetManager.API/Controllers/StrategyController.cs b/AssetManager.API/Controllers/StrategyController.cs index 403e6d8..4b1f994 100755 --- a/AssetManager.API/Controllers/StrategyController.cs +++ b/AssetManager.API/Controllers/StrategyController.cs @@ -28,7 +28,7 @@ public class StrategyController : ControllerBase return User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? throw new Exception("User not authenticated"); } - private StrategyListItemDTO MapToStrategyListItemDTO(Strategy strategy) + private StrategyListItemDto MapToStrategyListItemDTO(Strategy strategy) { List tags = new List(); if (!string.IsNullOrEmpty(strategy.Tags)) @@ -41,18 +41,18 @@ public class StrategyController : ControllerBase catch { } } - return new StrategyListItemDTO + return new StrategyListItemDto { - id = strategy.Id, - userId = strategy.UserId, - name = strategy.Alias, - type = strategy.Type, - description = strategy.Description, - tags = tags, - riskLevel = strategy.RiskLevel, - config = strategy.Config, - createdAt = strategy.CreatedAt, - updatedAt = strategy.UpdatedAt + Id = strategy.Id, + UserId = strategy.UserId, + Name = strategy.Alias, + Type = strategy.Type, + Description = strategy.Description, + Tags = tags, + RiskLevel = strategy.RiskLevel, + Config = strategy.Config, + CreatedAt = strategy.CreatedAt, + UpdatedAt = strategy.UpdatedAt }; } @@ -64,7 +64,7 @@ public class StrategyController : ControllerBase /// 此接口用于获取当前登录用户的所有策略列表。 /// [HttpGet("strategies")] - public ActionResult>> GetStrategies() + public ActionResult>> GetStrategies() { try { @@ -76,7 +76,7 @@ public class StrategyController : ControllerBase _logger.LogInformation("Strategies retrieved successfully"); - return Ok(new ApiResponse> + return Ok(new ApiResponse> { code = AssetManager.Models.StatusCodes.Success, data = strategyListItems, @@ -87,7 +87,7 @@ public class StrategyController : ControllerBase { _logger.LogError(ex, "Error retrieving strategies"); - return StatusCode(AssetManager.Models.StatusCodes.InternalServerError, new ApiResponse> + return StatusCode(AssetManager.Models.StatusCodes.InternalServerError, new ApiResponse> { code = AssetManager.Models.StatusCodes.InternalServerError, data = null, @@ -105,7 +105,7 @@ public class StrategyController : ControllerBase /// 此接口用于获取指定策略的详细信息。 /// [HttpGet("{id}")] - public ActionResult> GetStrategyById(string id) + public ActionResult> GetStrategyById(string id) { try { @@ -117,7 +117,7 @@ public class StrategyController : ControllerBase _logger.LogInformation($"Strategy {id} retrieved successfully"); - return Ok(new ApiResponse + return Ok(new ApiResponse { code = AssetManager.Models.StatusCodes.Success, data = strategyItem, @@ -128,7 +128,7 @@ public class StrategyController : ControllerBase { _logger.LogError(ex, $"Error retrieving strategy {id}"); - return StatusCode(AssetManager.Models.StatusCodes.InternalServerError, new ApiResponse + return StatusCode(AssetManager.Models.StatusCodes.InternalServerError, new ApiResponse { code = AssetManager.Models.StatusCodes.InternalServerError, data = null, @@ -146,11 +146,11 @@ public class StrategyController : ControllerBase /// 此接口用于创建新的策略。 /// [HttpPost] - public ActionResult> CreateStrategy([FromBody] CreateStrategyRequest request) + public ActionResult> CreateStrategy([FromBody] CreateStrategyRequest request) { try { - _logger.LogInformation($"Request to create strategy: {request.name}"); + _logger.LogInformation($"Request to create strategy: {request.Name}"); var userId = GetCurrentUserId(); var strategy = _strategyService.CreateStrategy(request, userId); @@ -158,7 +158,7 @@ public class StrategyController : ControllerBase _logger.LogInformation($"Strategy {strategy.Id} created successfully"); - return Ok(new ApiResponse + return Ok(new ApiResponse { code = AssetManager.Models.StatusCodes.Success, data = strategyItem, @@ -169,7 +169,7 @@ public class StrategyController : ControllerBase { _logger.LogError(ex, "Error creating strategy"); - return StatusCode(AssetManager.Models.StatusCodes.InternalServerError, new ApiResponse + return StatusCode(AssetManager.Models.StatusCodes.InternalServerError, new ApiResponse { code = AssetManager.Models.StatusCodes.InternalServerError, data = null, @@ -188,7 +188,7 @@ public class StrategyController : ControllerBase /// 此接口用于更新指定策略的信息。 /// [HttpPut("{id}")] - public ActionResult> UpdateStrategy(string id, [FromBody] UpdateStrategyRequest request) + public ActionResult> UpdateStrategy(string id, [FromBody] UpdateStrategyRequest request) { try { @@ -200,7 +200,7 @@ public class StrategyController : ControllerBase _logger.LogInformation($"Strategy {id} updated successfully"); - return Ok(new ApiResponse + return Ok(new ApiResponse { code = AssetManager.Models.StatusCodes.Success, data = strategyItem, @@ -211,7 +211,7 @@ public class StrategyController : ControllerBase { _logger.LogError(ex, $"Error updating strategy {id}"); - return StatusCode(AssetManager.Models.StatusCodes.InternalServerError, new ApiResponse + return StatusCode(AssetManager.Models.StatusCodes.InternalServerError, new ApiResponse { code = AssetManager.Models.StatusCodes.InternalServerError, data = null, diff --git a/AssetManager.Services/PortfolioService.cs b/AssetManager.Services/PortfolioService.cs index 74e7488..5073652 100755 --- a/AssetManager.Services/PortfolioService.cs +++ b/AssetManager.Services/PortfolioService.cs @@ -54,15 +54,15 @@ public class PortfolioService : IPortfolioService .Where(s => s.Id == request.StrategyId && s.UserId == userId) .First(); - if (strategy != null && !string.IsNullOrEmpty(strategy.Config)) + if (Strategy != null && !string.IsNullOrEmpty(Strategy.Config)) { try { // 风险平价策略 - if (strategy.Type?.Equals("risk_parity", StringComparison.OrdinalIgnoreCase) == true) + if (Strategy.Type?.Equals("risk_parity", StringComparison.OrdinalIgnoreCase) == true) { // 处理可能的双层转义 - string configJson = strategy.Config; + string configJson = Strategy.Config; if (configJson.StartsWith("\"") && configJson.EndsWith("\"")) { // 去掉外层的引号和转义 @@ -219,7 +219,7 @@ public class PortfolioService : IPortfolioService .Where(pos => pos.PortfolioId == portfolio.Id) .ToList(); - foreach (var pos in positions) + foreach (var pos in Positions) { if (pos.StockCode == null || pos.Currency == null) { @@ -235,7 +235,7 @@ public class PortfolioService : IPortfolioService if (priceResponse.Price > 0) { CurrentPrice = priceResponse.Price; - previousClose = priceResponse.PreviousClose > 0 ? priceResponse.PreviousClose : currentPrice; + previousClose = priceResponse.PreviousClose > 0 ? priceResponse.PreviousClose : CurrentPrice; } } catch (Exception ex) @@ -243,14 +243,14 @@ public class PortfolioService : IPortfolioService _logger.LogWarning(ex, "获取标的 {StockCode} 实时价格失败,使用成本价作为当前价", pos.StockCode); } - decimal currentPositionValue = pos.Shares * currentPrice; + decimal currentPositionValue = pos.Shares * CurrentPrice; decimal costPositionValue = pos.Shares * pos.AvgPrice; - decimal TodayProfit = previousClose > 0 ? pos.Shares * (currentPrice - previousClose) : 0; + decimal TodayProfit = previousClose > 0 ? pos.Shares * (CurrentPrice - previousClose) : 0; // 换算到目标本位币 decimal currentInTarget = await _exchangeRateService.ConvertAmountAsync(currentPositionValue, pos.Currency, targetCurrency); decimal costInTarget = await _exchangeRateService.ConvertAmountAsync(costPositionValue, pos.Currency, targetCurrency); - decimal todayProfitInTarget = await _exchangeRateService.ConvertAmountAsync(todayProfit, pos.Currency, targetCurrency); + decimal todayProfitInTarget = await _exchangeRateService.ConvertAmountAsync(TodayProfit, pos.Currency, targetCurrency); _logger.LogInformation("标的 {StockCode} 换算: {Amount} {From} → {Converted} {To},汇率: {Rate}", pos.StockCode, currentPositionValue, pos.Currency, currentInTarget, targetCurrency, @@ -302,7 +302,7 @@ public class PortfolioService : IPortfolioService decimal totalTodayProfit = 0; var positionItems = new List(); - foreach (var pos in positions) + foreach (var pos in Positions) { if (pos.StockCode == null || pos.Currency == null) { @@ -318,7 +318,7 @@ public class PortfolioService : IPortfolioService if (priceResponse.Price > 0) { CurrentPrice = priceResponse.Price; - previousClose = priceResponse.PreviousClose > 0 ? priceResponse.PreviousClose : currentPrice; + previousClose = priceResponse.PreviousClose > 0 ? priceResponse.PreviousClose : CurrentPrice; } } catch (Exception ex) @@ -326,16 +326,16 @@ public class PortfolioService : IPortfolioService _logger.LogWarning(ex, "获取标的 {StockCode} 实时价格失败,使用成本价作为当前价", pos.StockCode); } - decimal positionValue = pos.Shares * currentPrice; + decimal positionValue = pos.Shares * CurrentPrice; decimal cost = pos.Shares * pos.AvgPrice; decimal Profit = positionValue - cost; - double ProfitRate = cost > 0 ? (double)(profit / cost * 100) : 0; - decimal TodayProfit = previousClose > 0 ? pos.Shares * (currentPrice - previousClose) : 0; + double ProfitRate = cost > 0 ? (double)(Profit / cost * 100) : 0; + decimal TodayProfit = previousClose > 0 ? pos.Shares * (CurrentPrice - previousClose) : 0; // 转换为组合本位币 decimal positionValueInTarget = await _exchangeRateService.ConvertAmountAsync(positionValue, pos.Currency, targetCurrency); decimal costInTarget = await _exchangeRateService.ConvertAmountAsync(cost, pos.Currency, targetCurrency); - decimal todayProfitInTarget = await _exchangeRateService.ConvertAmountAsync(todayProfit, pos.Currency, targetCurrency); + decimal todayProfitInTarget = await _exchangeRateService.ConvertAmountAsync(TodayProfit, pos.Currency, targetCurrency); totalPortfolioValue += positionValueInTarget; totalCost += costInTarget; @@ -349,10 +349,10 @@ public class PortfolioService : IPortfolioService Symbol = pos.StockCode, Amount = (int)pos.Shares, AveragePrice = (double)pos.AvgPrice, - CurrentPrice = (double)currentPrice, + CurrentPrice = (double)TodayProfit, TotalValue = (double)positionValueInTarget, Profit = (double)(positionValueInTarget - costInTarget), - ProfitRate = profitRate, + ProfitRate = ProfitRate, ChangeAmount = (double)todayProfitInTarget, Ratio = 0, // 后面统一计算比例 DeviationRatio = 0, // 后续实现 @@ -367,7 +367,7 @@ public class PortfolioService : IPortfolioService } decimal TotalReturn = totalPortfolioValue - totalCost; - double totalReturnRate = totalCost > 0 ? (double)(totalReturn / totalCost * 100) : 0; + double totalReturnRate = totalCost > 0 ? (double)(TotalReturn / totalCost * 100) : 0; return new PortfolioDetailResponse { @@ -382,7 +382,7 @@ public class PortfolioService : IPortfolioService Description = "策略描述" }, PortfolioValue = (double)totalPortfolioValue, - TotalReturn = (double)totalReturn, + TotalReturn = (double)TotalReturn, TodayProfit = (double)totalTodayProfit, HistoricalChange = totalReturnRate, DailyVolatility = 0, // 后续实现 @@ -390,7 +390,7 @@ public class PortfolioService : IPortfolioService LogicModel = "HFEA 风险平价逻辑", LogicModelStatus = "监控中", LogicModelDescription = "目标权重 季度调仓", - TotalItems = positions.Count, + TotalItems = Positions.Count, TotalRatio = 100.0, Positions = positionItems }; @@ -576,7 +576,7 @@ public class PortfolioService : IPortfolioService .ToList(); decimal totalPortfolioValue = 0; - foreach (var pos in positions) + foreach (var pos in Positions) { if (pos.StockCode == null) { @@ -598,7 +598,7 @@ public class PortfolioService : IPortfolioService _logger.LogWarning(ex, "获取标的 {StockCode} 实时价格失败,使用成本价计算组合总价值", pos.StockCode); } - totalPortfolioValue += pos.Shares * currentPrice; + totalPortfolioValue += pos.Shares * CurrentPrice; } portfolio.TotalValue = totalPortfolioValue; @@ -648,7 +648,7 @@ public class PortfolioService : IPortfolioService public Task> GetTransactionsAsync(string portfolioId, GetTransactionsRequest request, string userId) { - var response = GetTransactions(portfolioId, userId, request.limit, request.offset); + var response = GetTransactions(portfolioId, userId, request.Limit, request.Offset); return Task.FromResult(response.Items ?? new List()); } @@ -659,7 +659,7 @@ public class PortfolioService : IPortfolioService return new TransactionItem { - Id = response.id, + Id = response.Id, PortfolioId = portfolioId, Date = DateTime.Now.ToString("yyyy-MM-dd"), Time = DateTime.Now.ToString("HH:mm:ss"), @@ -667,7 +667,7 @@ public class PortfolioService : IPortfolioService StockCode = request.StockCode, Amount = response.TotalAmount, Currency = request.Currency, - Status = response.status, + Status = response.Status, Remark = request.Remark }; } @@ -687,9 +687,9 @@ public class PortfolioService : IPortfolioService var Positions = _db.Queryable() .Where(pos => pos.PortfolioId == portfolioId) .ToList(); - if (positions.Any()) + if (Positions.Any()) { - _db.Deleteable(positions).ExecuteCommand(); + _db.Deleteable(Positions).ExecuteCommand(); } // 删除相关交易 diff --git a/AssetManager.Tests/Repositories/PortfolioRepositoryTests.cs b/AssetManager.Tests/Repositories/PortfolioRepositoryTests.cs index 3bfc721..ea5fe6a 100644 --- a/AssetManager.Tests/Repositories/PortfolioRepositoryTests.cs +++ b/AssetManager.Tests/Repositories/PortfolioRepositoryTests.cs @@ -1,6 +1,7 @@ using AssetManager.Data; using AssetManager.Data.Repositories; using FluentAssertions; +using Microsoft.Extensions.Logging; using Moq; using SqlSugar; using Xunit; diff --git a/AssetManager.Tests/Services/PortfolioServiceTests.cs b/AssetManager.Tests/Services/PortfolioServiceTests.cs index 7adc58b..7e8a612 100644 --- a/AssetManager.Tests/Services/PortfolioServiceTests.cs +++ b/AssetManager.Tests/Services/PortfolioServiceTests.cs @@ -3,6 +3,7 @@ using AssetManager.Infrastructure.Services; using AssetManager.Models.DTOs; using AssetManager.Services; using FluentAssertions; +using Microsoft.Extensions.Logging; using Moq; using SqlSugar; using Xunit;