From d605809932aca02a29c6e90a717a0c1de39752c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A9=99=E5=AD=90?= <454313500@qq.com> Date: Sun, 9 Mar 2025 16:21:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E5=96=84=E5=AF=B9=E6=8E=A5?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Services/StockHoldingService.cs | 1 + .../Services/StockMarketService.cs | 2 +- .../StockTransactionEventHandler.cs | 42 ++++ .../Managers/StockMarketManager.cs | 45 ++-- .../Services/StockMarketService.cs | 0 Yi.Bbs.Vue3/src/apis/stockApi.js | 18 ++ Yi.Bbs.Vue3/src/apis/userApi.js | 4 +- Yi.Bbs.Vue3/src/router/index.js | 17 +- Yi.Bbs.Vue3/src/views/stock/Index.vue | 237 +++++++++++++++++- 9 files changed, 311 insertions(+), 55 deletions(-) create mode 100644 Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Domain/EventHandlers/StockTransactionEventHandler.cs create mode 100644 Yi.Abp.Net8/module/stock/Yi.Framework.Stock.Application/Services/StockMarketService.cs diff --git a/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Application/Services/StockHoldingService.cs b/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Application/Services/StockHoldingService.cs index ff692258..4f1f8959 100644 --- a/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Application/Services/StockHoldingService.cs +++ b/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Application/Services/StockHoldingService.cs @@ -56,6 +56,7 @@ namespace Yi.Framework.Stock.Application.Services StockId = h.StockId, StockCode = h.StockCode, StockName = h.StockName, + Quantity = h.Quantity, CreationTime = h.CreationTime }) .ToPageListAsync(input.SkipCount, input.MaxResultCount, total); diff --git a/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Application/Services/StockMarketService.cs b/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Application/Services/StockMarketService.cs index d2df8d3e..bf1c8115 100644 --- a/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Application/Services/StockMarketService.cs +++ b/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Application/Services/StockMarketService.cs @@ -137,7 +137,7 @@ namespace Yi.Framework.Stock.Application.Services /// /// 卖出股票 /// - [HttpPost("stock/sell")] + [HttpDelete("stock/sell")] [Authorize] public async Task SellStockAsync(SellStockInputDto input) { diff --git a/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Domain/EventHandlers/StockTransactionEventHandler.cs b/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Domain/EventHandlers/StockTransactionEventHandler.cs new file mode 100644 index 00000000..75fdc3c1 --- /dev/null +++ b/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Domain/EventHandlers/StockTransactionEventHandler.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus; +using Yi.Framework.Stock.Domain.Entities; +using Yi.Framework.Stock.Domain.Shared.Etos; +using Yi.Framework.SqlSugarCore.Abstractions; + +namespace Yi.Framework.Stock.Domain.EventHandlers +{ + /// + /// 股票交易事件处理器 + /// + public class StockTransactionEventHandler : ILocalEventHandler, ITransientDependency + { + private readonly ISqlSugarRepository _transactionRepository; + + public StockTransactionEventHandler( + ISqlSugarRepository transactionRepository) + { + _transactionRepository = transactionRepository; + } + + public async Task HandleEventAsync(StockTransactionEto eventData) + { + // 创建交易记录实体 + var transaction = new StockTransactionEntity( + eventData.UserId, + eventData.StockId, + eventData.StockCode, + eventData.StockName, + eventData.TransactionType, + eventData.Price, + eventData.Quantity, + eventData.Fee + ); + + // 保存交易记录 + await _transactionRepository.InsertAsync(transaction); + } + } +} \ No newline at end of file diff --git a/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Domain/Managers/StockMarketManager.cs b/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Domain/Managers/StockMarketManager.cs index e9f086c8..532e33e6 100644 --- a/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Domain/Managers/StockMarketManager.cs +++ b/Yi.Abp.Net8/module/ai-stock/Yi.Framework.Stock.Domain/Managers/StockMarketManager.cs @@ -10,6 +10,7 @@ using Yi.Framework.Stock.Domain.Shared.Etos; using Yi.Framework.SqlSugarCore.Abstractions; using Yi.Framework.Stock.Domain.Managers.SemanticKernel; using Yi.Framework.Stock.Domain.Managers.SemanticKernel.Plugins; +using Microsoft.Extensions.Hosting; namespace Yi.Framework.Stock.Domain.Managers { @@ -27,12 +28,16 @@ namespace Yi.Framework.Stock.Domain.Managers private readonly ISqlSugarRepository _stockMarketRepository; private readonly ILocalEventBus _localEventBus; private readonly SemanticKernelClient _skClient; + private readonly IHostEnvironment _hostEnvironment; + public StockMarketManager( ISqlSugarRepository stockHoldingRepository, ISqlSugarRepository stockTransactionRepository, ISqlSugarRepository stockPriceRecordRepository, ISqlSugarRepository stockMarketRepository, - ILocalEventBus localEventBus, SemanticKernelClient skClient) + ILocalEventBus localEventBus, + SemanticKernelClient skClient, + IHostEnvironment hostEnvironment) { _stockHoldingRepository = stockHoldingRepository; _stockTransactionRepository = stockTransactionRepository; @@ -40,6 +45,7 @@ namespace Yi.Framework.Stock.Domain.Managers _stockMarketRepository = stockMarketRepository; _localEventBus = localEventBus; _skClient = skClient; + _hostEnvironment = hostEnvironment; } /// @@ -103,20 +109,6 @@ namespace Yi.Framework.Stock.Domain.Managers 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 { @@ -189,19 +181,6 @@ namespace Yi.Framework.Stock.Domain.Managers 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 { @@ -246,8 +225,8 @@ namespace Yi.Framework.Stock.Domain.Managers /// 手续费 private decimal CalculateTradingFee(decimal amount, TransactionTypeEnum transactionType) { - // 示例费率:买入0.1%,卖出0.2% - decimal feeRate = transactionType == TransactionTypeEnum.Buy ? 0.001m : 0.002m; + // 买入不收手续费,卖出收2% + decimal feeRate = transactionType == TransactionTypeEnum.Buy ? 0m : 0.02m; return amount * feeRate; } @@ -257,6 +236,12 @@ namespace Yi.Framework.Stock.Domain.Managers /// 如果不在允许卖出的时间范围内 private void VerifySellTime() { + // 如果是开发环境,跳过验证 + if (_hostEnvironment.IsDevelopment()) + { + return; + } + DateTime now = DateTime.Now; // 检查是否为工作日(周一到周五) diff --git a/Yi.Abp.Net8/module/stock/Yi.Framework.Stock.Application/Services/StockMarketService.cs b/Yi.Abp.Net8/module/stock/Yi.Framework.Stock.Application/Services/StockMarketService.cs new file mode 100644 index 00000000..e69de29b diff --git a/Yi.Bbs.Vue3/src/apis/stockApi.js b/Yi.Bbs.Vue3/src/apis/stockApi.js index 3a9681fa..c57d3863 100644 --- a/Yi.Bbs.Vue3/src/apis/stockApi.js +++ b/Yi.Bbs.Vue3/src/apis/stockApi.js @@ -46,4 +46,22 @@ export function getStockMarkets() { url: "/stock/markets", method: "get" }); +} + +// 买入股票 +export function buyStock(data) { + return request({ + url: "/stock/buy", + method: "post", + data + }); +} + +// 卖出股票 +export function sellStock(params) { + return request({ + url: "/stock/sell", + method: "delete", + params + }); } \ No newline at end of file diff --git a/Yi.Bbs.Vue3/src/apis/userApi.js b/Yi.Bbs.Vue3/src/apis/userApi.js index f38fc88c..747c0968 100644 --- a/Yi.Bbs.Vue3/src/apis/userApi.js +++ b/Yi.Bbs.Vue3/src/apis/userApi.js @@ -75,9 +75,9 @@ export function getUserProfile() { } // 查询bbs个人信息 -export function getBbsUserProfile(userName) { +export function getBbsUserProfile(userNameOrId) { return request({ - url: `/bbs-user/${userName}`, + url: `/bbs-user/${userNameOrId}`, method: "get", }); } diff --git a/Yi.Bbs.Vue3/src/router/index.js b/Yi.Bbs.Vue3/src/router/index.js index 984d389d..ffdfcf51 100644 --- a/Yi.Bbs.Vue3/src/router/index.js +++ b/Yi.Bbs.Vue3/src/router/index.js @@ -144,14 +144,6 @@ const router = createRouter({ title: "面试宝典", }, }, - { - name: "stock", - path: "/stock", - component: () => import("../views/stock/Index.vue"), - meta: { - title: "股票", - }, - }, ], }, { @@ -203,7 +195,14 @@ const router = createRouter({ }, ], }, - + { + name: "stock", + path: "/stock", + component: () => import("../views/stock/Index.vue"), + meta: { + title: "股票", + }, + }, { path: "/hub", name: "hub", diff --git a/Yi.Bbs.Vue3/src/views/stock/Index.vue b/Yi.Bbs.Vue3/src/views/stock/Index.vue index bf34afcc..f3cee2f8 100644 --- a/Yi.Bbs.Vue3/src/views/stock/Index.vue +++ b/Yi.Bbs.Vue3/src/views/stock/Index.vue @@ -7,6 +7,8 @@
+
当前钱钱: {{ userInfo.money || 0 }}
+ {{ item.code }} + + + 返回社区 +
@@ -44,6 +55,7 @@ v-for="news in newsList" :key="news.id" class="news-item" + @click="showNewsDetail(news)" >
{{ dayjs(news.publishTime).format('YYYY-MM-DD') }} @@ -144,7 +156,7 @@

我的持仓

- 总资产: ¥{{ totalAssets }} + 持有钱钱: ¥{{ totalAssets }}
@@ -170,14 +182,52 @@
+ + + +
+ {{ dayjs(currentNewsDetail.publishTime).format('YYYY-MM-DD HH:mm:ss') }} + 来源: {{ currentNewsDetail.source }} +
+
{{ currentNewsDetail.content }}
+
@@ -469,8 +599,9 @@ onMounted(async () => { } .selector-area { - flex: 0 0 150px; - text-align: right; + display: flex; + align-items: center; + gap: 10px; } .title { @@ -803,4 +934,84 @@ onMounted(async () => { margin-top: 5px; font-size: 12px; } + +/* 新闻详情弹窗样式 */ +.news-detail-dialog :deep(.el-dialog__header) { + padding: 15px 20px; + border-bottom: 1px solid #30363d; +} + +.news-detail-dialog :deep(.el-dialog__title) { + font-size: 18px; + font-weight: bold; + color: #ffffff !important; + text-shadow: 0 0 3px rgba(255, 255, 255, 0.3); +} + +.news-detail-dialog :deep(.el-dialog__body) { + padding: 20px; + color: #e6edf3; +} + + +.news-detail-header { + display: flex; + justify-content: space-between; + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid #30363d; + color: #8b949e; + font-size: 0.9em; +} + +.news-detail-content { + line-height: 1.6; + white-space: pre-line; +} + +/* 修改Element弹窗适应深色主题 */ +.stock-dashboard :deep(.el-dialog) { + background-color: #161b22; + border: 1px solid #30363d; + border-radius: 8px; + box-shadow: 0 0 15px rgba(0, 0, 0, 0.5); +} + +.stock-dashboard :deep(.el-dialog__headerbtn .el-dialog__close) { + color: #8b949e; +} + +.stock-dashboard :deep(.el-dialog__headerbtn:hover .el-dialog__close) { + color: #58a6ff; +} + +.return-button { + margin-left: 10px; + background-color: #58a6ff; + border-color: #58a6ff; + transition: all 0.3s; +} + +.return-button:hover { + background-color: #388bfd; + border-color: #388bfd; + transform: translateY(-2px); + box-shadow: 0 2px 5px rgba(88, 166, 255, 0.4); +} + +.user-money { + color: #e6edf3; + font-size: 14px; + margin-right: 15px; + padding: 4px 8px; + background-color: #21262d; + border-radius: 4px; + border: 1px solid #30363d; +} + +.money-value { + font-weight: bold; + color: #7ee787; + margin-left: 4px; +}