Files
Yi.Admin/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Domain/Managers/StockMarketManager.cs
2025-03-05 23:08:58 +08:00

323 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Services;
using Volo.Abp.EventBus.Local;
using Yi.Framework.Bbs.Domain.Shared.Etos;
using Yi.Framework.Stock.Domain.Entities;
using Yi.Framework.Stock.Domain.Shared;
using Yi.Framework.Stock.Domain.Shared.Etos;
using Yi.Framework.SqlSugarCore.Abstractions;
using Yi.Framework.Stock.Domain.Managers.Plugins;
namespace Yi.Framework.Stock.Domain.Managers
{
/// <summary>
/// 股市领域服务
/// </summary>
/// <remarks>
/// 处理股票交易相关业务,例如买入、卖出等
/// </remarks>
public class StockMarketManager : DomainService
{
private readonly ISqlSugarRepository<StockHoldingAggregateRoot> _stockHoldingRepository;
private readonly ISqlSugarRepository<StockTransactionEntity> _stockTransactionRepository;
private readonly ISqlSugarRepository<StockPriceRecordEntity> _stockPriceRecordRepository;
private readonly ISqlSugarRepository<StockMarketAggregateRoot> _stockMarketRepository;
private readonly ILocalEventBus _localEventBus;
private readonly SemanticKernelClient _skClient;
public StockMarketManager(
ISqlSugarRepository<StockHoldingAggregateRoot> stockHoldingRepository,
ISqlSugarRepository<StockTransactionEntity> stockTransactionRepository,
ISqlSugarRepository<StockPriceRecordEntity> stockPriceRecordRepository,
ISqlSugarRepository<StockMarketAggregateRoot> stockMarketRepository,
ILocalEventBus localEventBus, SemanticKernelClient skClient)
{
_stockHoldingRepository = stockHoldingRepository;
_stockTransactionRepository = stockTransactionRepository;
_stockPriceRecordRepository = stockPriceRecordRepository;
_stockMarketRepository = stockMarketRepository;
_localEventBus = localEventBus;
_skClient = skClient;
}
/// <summary>
/// 购买股票
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="stockId">股票ID</param>
/// <param name="quantity">购买数量</param>
/// <returns></returns>
public async Task BuyStockAsync(Guid userId, Guid stockId, int quantity)
{
if (quantity <= 0)
{
throw new UserFriendlyException("购买数量必须大于0");
}
// 通过stockId查询获取股票信息
var stockInfo = await _stockMarketRepository.GetFirstAsync(s => s.Id == stockId);
if (stockInfo == null)
{
throw new UserFriendlyException("找不到指定的股票");
}
string stockCode = stockInfo.MarketCode; // 根据实际字段调整
string stockName = stockInfo.MarketName; // 根据实际字段调整
// 获取当前股票价格
decimal currentPrice = await GetCurrentStockPriceAsync(stockId);
// 计算总金额和手续费
decimal totalAmount = currentPrice * quantity;
decimal fee = CalculateTradingFee(totalAmount, TransactionTypeEnum.Buy);
decimal totalCost = totalAmount + fee;
// 扣减用户资金
await _localEventBus.PublishAsync(
new MoneyChangeEventArgs { UserId = userId, Number = -totalCost }, false);
// 更新或创建用户持仓
var holding = await _stockHoldingRepository.GetFirstAsync(h =>
h.UserId == userId &&
h.StockId == stockId &&
!h.IsDeleted);
if (holding == null)
{
// 创建新持仓
holding = new StockHoldingAggregateRoot(
userId,
stockId,
stockCode,
stockName,
quantity,
currentPrice);
await _stockHoldingRepository.InsertAsync(holding);
}
else
{
// 更新现有持仓
holding.AddQuantity(quantity, currentPrice);
await _stockHoldingRepository.UpdateAsync(holding);
}
// 创建交易记录
var transaction = new StockTransactionEntity(
userId,
stockId,
stockCode,
stockName,
TransactionTypeEnum.Buy,
currentPrice,
quantity,
fee);
await _stockTransactionRepository.InsertAsync(transaction);
// 发布交易事件
await _localEventBus.PublishAsync(new StockTransactionEto
{
UserId = userId,
StockId = stockId,
StockCode = stockCode,
StockName = stockName,
TransactionType = TransactionTypeEnum.Buy,
Price = currentPrice,
Quantity = quantity,
TotalAmount = totalAmount,
Fee = fee
}, false);
}
/// <summary>
/// 卖出股票
/// </summary>
/// <param name="userId">用户ID</param>
/// <param name="stockId">股票ID</param>
/// <param name="quantity">卖出数量</param>
/// <returns></returns>
public async Task SellStockAsync(Guid userId, Guid stockId, int quantity)
{
// 验证卖出时间
VerifySellTime();
if (quantity <= 0)
{
throw new UserFriendlyException("卖出数量必须大于0");
}
// 获取用户持仓
var holding = await _stockHoldingRepository.GetFirstAsync(h =>
h.UserId == userId &&
h.StockId == stockId &&
!h.IsDeleted);
if (holding == null)
{
throw new UserFriendlyException("您没有持有该股票");
}
if (holding.Quantity < quantity)
{
throw new UserFriendlyException("持仓数量不足");
}
// 获取当前股票价格
decimal currentPrice = await GetCurrentStockPriceAsync(stockId);
// 计算总金额和手续费
decimal totalAmount = currentPrice * quantity;
decimal fee = CalculateTradingFee(totalAmount, TransactionTypeEnum.Sell);
decimal actualIncome = totalAmount - fee;
// 增加用户资金
await _localEventBus.PublishAsync(
new MoneyChangeEventArgs { UserId = userId, Number = actualIncome }, false);
// 更新用户持仓
holding.ReduceQuantity(quantity);
if (holding.Quantity > 0)
{
await _stockHoldingRepository.UpdateAsync(holding);
}
else
{
await _stockHoldingRepository.DeleteAsync(holding);
}
// 创建交易记录
var transaction = new StockTransactionEntity(
userId,
stockId,
holding.StockCode,
holding.StockName,
TransactionTypeEnum.Sell,
currentPrice,
quantity,
fee);
await _stockTransactionRepository.InsertAsync(transaction);
// 发布交易事件
await _localEventBus.PublishAsync(new StockTransactionEto
{
UserId = userId,
StockId = stockId,
StockCode = holding.StockCode,
StockName = holding.StockName,
TransactionType = TransactionTypeEnum.Sell,
Price = currentPrice,
Quantity = quantity,
TotalAmount = totalAmount,
Fee = fee
}, false);
}
/// <summary>
/// 获取股票当前价格
/// </summary>
/// <param name="stockId">股票ID</param>
/// <returns>当前价格</returns>
public async Task<decimal> GetCurrentStockPriceAsync(Guid stockId)
{
// 获取最新的价格记录
var latestPriceRecord = await _stockPriceRecordRepository._DbQueryable
.Where(p => p.StockId == stockId)
.OrderByDescending(p => p.CreationTime)
.FirstAsync();
if (latestPriceRecord == null)
{
throw new UserFriendlyException("无法获取股票价格信息");
}
return latestPriceRecord.CurrentPrice;
}
/// <summary>
/// 计算交易手续费
/// </summary>
/// <param name="amount">交易金额</param>
/// <param name="transactionType">交易类型</param>
/// <returns>手续费</returns>
private decimal CalculateTradingFee(decimal amount, TransactionTypeEnum transactionType)
{
// 示例费率买入0.1%卖出0.2%
decimal feeRate = transactionType == TransactionTypeEnum.Buy ? 0.001m : 0.002m;
return amount * feeRate;
}
/// <summary>
/// 验证卖出时间
/// </summary>
/// <exception cref="UserFriendlyException">如果不在允许卖出的时间范围内</exception>
private void VerifySellTime()
{
DateTime now = DateTime.Now;
// 检查是否为工作日(周一到周五)
if (now.DayOfWeek == DayOfWeek.Saturday || now.DayOfWeek == DayOfWeek.Sunday)
{
throw new UserFriendlyException("股票只能在工作日(周一至周五)卖出");
}
// 检查是否在下午5点到6点之间
if (now.Hour < 17 || now.Hour >= 18)
{
throw new UserFriendlyException("股票只能在下午5点到6点之间卖出");
}
}
/// <summary>
/// 批量保存多个股票的最新价格记录
/// </summary>
/// <param name="priceRecords">价格记录列表</param>
/// <returns>保存的记录数量</returns>
public async Task<int> BatchSaveStockPriceRecordsAsync(List<StockPriceRecordEntity> priceRecords)
{
if (priceRecords == null || !priceRecords.Any())
{
return 0;
}
// 验证数据
foreach (var record in priceRecords)
{
if (record.CurrentPrice <= 0)
{
throw new UserFriendlyException($"股票ID {record.StockId} 的价格必须大于0");
}
// 设置创建时间为当前时间(如果未设置)
if (record.CreationTime == default)
{
record.CreationTime = DateTime.Now;
}
// 计算交易额(如果未设置)
if (record.Turnover == 0 && record.Volume > 0)
{
record.Turnover = record.CurrentPrice * record.Volume;
}
}
await _stockPriceRecordRepository.InsertManyAsync(priceRecords);
return priceRecords.Count;
}
/// <summary>
/// 生成最新股票记录
/// </summary>
/// <returns></returns>
public async Task GenerateStocksAsync()
{
_skClient.RegisterPlugins<StockPlugins>("stock");
await _skClient.ChatCompletionAsync("帮我生成多个股市内容");
}
}
}