AssetManager.API/AssetManager.Services/PortfolioService.cs
niannian zheng 564687bc1e refactor: 移除投资组合详情中的交易记录功能
重构投资组合详情响应,移除了不再需要的交易记录相关字段和逻辑
更新了README文档以反映API变更
2026-03-02 15:37:06 +08:00

312 lines
10 KiB
C#

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<PortfolioListItem> GetPortfolios(string userId)
{
var portfolios = _db.Queryable<Portfolio>()
.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<Portfolio>()
.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<Portfolio>()
.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<Position>()
.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<Portfolio>()
.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<Transaction>()
.Where(t => t.PortfolioId == portfolioId)
.OrderByDescending(t => t.TransactionTime)
.Skip(offset)
.Take(limit)
.ToList();
var total = _db.Queryable<Transaction>()
.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<Portfolio>()
.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<Position>()
.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<Position>()
.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")
};
}
}