using AssetManager.Data; using AssetManager.Models.DTOs; using SqlSugar; namespace AssetManager.Services; public class PortfolioService : IPortfolioService { private readonly ISqlSugarClient _db; public PortfolioService(ISqlSugarClient db) { _db = db; } public CreatePortfolioResponse CreatePortfolio(CreatePortfolioRequest request, string userId) { var portfolio = new Portfolio { Id = "port-" + Guid.NewGuid().ToString().Substring(0, 8), UserId = userId, StrategyId = request.strategyId, Name = request.name, Currency = request.currency, TotalValue = (decimal)request.stocks.Sum(s => s.price * s.amount), ReturnRate = 0, Status = "运行中", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; _db.Insertable(portfolio).ExecuteCommand(); // 创建初始持仓 foreach (var stock in request.stocks) { var position = new Position { Id = "pos-" + Guid.NewGuid().ToString().Substring(0, 8), PortfolioId = portfolio.Id, StockCode = stock.code, StockName = stock.name, AssetType = "Stock", Shares = (decimal)stock.amount, AvgPrice = (decimal)stock.price, Currency = request.currency, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; _db.Insertable(position).ExecuteCommand(); // 创建交易记录 var transaction = new Transaction { Id = "trans-" + Guid.NewGuid().ToString().Substring(0, 8), PortfolioId = portfolio.Id, Type = "buy", StockCode = stock.code, AssetType = "Stock", Title = "初始建仓", Amount = (decimal)stock.amount, Price = (decimal)stock.price, TotalAmount = (decimal)(stock.price * stock.amount), Currency = request.currency, Status = "completed", Remark = "初始建仓", TransactionTime = DateTime.Now, CreatedAt = DateTime.Now }; _db.Insertable(transaction).ExecuteCommand(); } return new CreatePortfolioResponse { id = portfolio.Id, totalValue = (double)portfolio.TotalValue, returnRate = 0, currency = portfolio.Currency, createdAt = portfolio.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss") }; } public List GetPortfolios(string userId) { var portfolios = _db.Queryable() .Where(p => p.UserId == userId) .ToList(); return portfolios.Select(p => new PortfolioListItem { id = p.Id, name = p.Name, tags = $"{p.Status} · {p.Currency}", status = p.Status, statusType = p.Status == "运行中" ? "green" : "gray", iconChar = p.Name.Substring(0, 1).ToUpper(), iconBgClass = "bg-blue-100", iconTextClass = "text-blue-700", value = (double)p.TotalValue, currency = p.Currency, returnRate = (double)p.ReturnRate, returnType = p.ReturnRate >= 0 ? "positive" : "negative" }).ToList(); } public TotalAssetsResponse GetTotalAssets(string userId) { var totalValue = _db.Queryable() .Where(p => p.UserId == userId) .Sum(p => p.TotalValue); // 这里简化处理,实际应该计算今日盈亏和总收益率 return new TotalAssetsResponse { totalValue = (double)totalValue, currency = "CNY", // 假设统一转换为CNY todayProfit = 0, todayProfitCurrency = "CNY", totalReturnRate = 0 }; } public PortfolioDetailResponse GetPortfolioById(string id, string userId) { var portfolio = _db.Queryable() .Where(p => p.Id == id && p.UserId == userId) .First(); if (portfolio == null) { throw new Exception("Portfolio not found or access denied"); } var positions = _db.Queryable() .Where(pos => pos.PortfolioId == id) .ToList(); return new PortfolioDetailResponse { id = portfolio.Id, name = portfolio.Name, currency = portfolio.Currency, strategy = new StrategyInfo { id = portfolio.StrategyId, name = "策略名称", description = "策略描述" }, portfolioValue = (double)portfolio.TotalValue, totalReturn = (double)(portfolio.TotalValue * portfolio.ReturnRate), todayProfit = 0, todayProfitCurrency = portfolio.Currency, positions = positions.Select(pos => new PositionItem { id = pos.Id, stockCode = pos.StockCode, stockName = pos.StockName, amount = (int)pos.Shares, averagePrice = (double)pos.AvgPrice, currentPrice = (double)pos.AvgPrice, totalValue = (double)(pos.Shares * pos.AvgPrice), profit = 0, profitRate = 0, currency = pos.Currency }).ToList() }; } public GetTransactionsResponse GetTransactions(string portfolioId, string userId, int limit, int offset) { // 验证投资组合是否属于该用户 var portfolio = _db.Queryable() .Where(p => p.Id == portfolioId && p.UserId == userId) .First(); if (portfolio == null) { throw new Exception("Portfolio not found or access denied"); } var transactions = _db.Queryable() .Where(t => t.PortfolioId == portfolioId) .OrderByDescending(t => t.TransactionTime) .Skip(offset) .Take(limit) .ToList(); var total = _db.Queryable() .Where(t => t.PortfolioId == portfolioId) .Count(); return new GetTransactionsResponse { items = transactions.Select(t => new TransactionItem { id = t.Id, portfolioId = t.PortfolioId, date = t.TransactionTime.ToString("yyyy-MM-dd"), time = t.TransactionTime.ToString("HH:mm:ss"), type = t.Type, title = t.Title, amount = (double)t.TotalAmount, currency = t.Currency, status = t.Status, remark = t.Remark }).ToList(), total = total, page = offset / limit + 1, pageSize = limit }; } public CreateTransactionResponse CreateTransaction(CreateTransactionRequest request, string userId) { // 验证投资组合是否属于该用户 var portfolio = _db.Queryable() .Where(p => p.Id == request.portfolioId && p.UserId == userId) .First(); if (portfolio == null) { throw new Exception("Portfolio not found or access denied"); } var transaction = new Transaction { Id = "trans-" + Guid.NewGuid().ToString().Substring(0, 8), PortfolioId = request.portfolioId, Type = request.type, StockCode = request.stockCode, AssetType = "Stock", Title = request.remark ?? "交易", Amount = (decimal)request.amount, Price = (decimal)request.price, TotalAmount = (decimal)(request.price * request.amount), Currency = request.currency, Status = "processing", Remark = request.remark, TransactionTime = DateTime.Now, CreatedAt = DateTime.Now }; _db.Insertable(transaction).ExecuteCommand(); // 更新持仓 var position = _db.Queryable() .Where(pos => pos.PortfolioId == request.portfolioId && pos.StockCode == request.stockCode) .First(); if (position != null) { if (request.type == "buy") { // 计算新的平均价格 var newTotalShares = position.Shares + (decimal)request.amount; var newTotalCost = (position.Shares * position.AvgPrice) + ((decimal)request.amount * (decimal)request.price); position.AvgPrice = newTotalCost / newTotalShares; position.Shares = newTotalShares; } else if (request.type == "sell") { position.Shares -= (decimal)request.amount; if (position.Shares <= 0) { _db.Deleteable(position).ExecuteCommand(); } else { _db.Updateable(position).ExecuteCommand(); } } } else if (request.type == "buy") { // 创建新持仓 position = new Position { Id = "pos-" + Guid.NewGuid().ToString().Substring(0, 8), PortfolioId = request.portfolioId, StockCode = request.stockCode, StockName = request.remark ?? request.stockCode, AssetType = "Stock", Shares = (decimal)request.amount, AvgPrice = (decimal)request.price, Currency = request.currency, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; _db.Insertable(position).ExecuteCommand(); } // 更新投资组合总价值 var totalValue = _db.Queryable() .Where(pos => pos.PortfolioId == request.portfolioId) .Sum(pos => pos.Shares * pos.AvgPrice); portfolio.TotalValue = totalValue; portfolio.UpdatedAt = DateTime.Now; _db.Updateable(portfolio).ExecuteCommand(); return new CreateTransactionResponse { id = transaction.Id, totalAmount = (double)transaction.TotalAmount, status = transaction.Status, createdAt = transaction.CreatedAt.ToString("yyyy-MM-dd HH:mm:ss") }; } }