From 7e27996f1738782d9cd9a1aeaf4d5eef8d2623f6 Mon Sep 17 00:00:00 2001 From: spark Date: Thu, 9 Jan 2025 14:45:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(backend):=20=E4=BC=98=E5=8C=96=E8=82=A1?= =?UTF-8?q?=E7=A5=A8=E6=95=B0=E6=8D=AE=E8=8E=B7=E5=8F=96=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 GetStockCodeRealTimeData 方法,支持批量获取多个股票代码的实时数据 - 新增 GetStockInfos 函数,用于获取关注股票的实时信息- 重构 getStockInfo 函数,提高代码复用性 - 优化数据处理逻辑,提高程序运行效率 --- app.go | 62 +++++++++++++++++++++++------ backend/data/stock_data_api.go | 25 +++++++++--- backend/data/stock_data_api_test.go | 28 ++++++++++++- frontend/src/components/stock.vue | 4 +- 4 files changed, 97 insertions(+), 22 deletions(-) diff --git a/app.go b/app.go index 8c8c446..074aa65 100644 --- a/app.go +++ b/app.go @@ -8,6 +8,7 @@ import ( "github.com/coocood/freecache" "github.com/duke-git/lancet/v2/convertor" "github.com/duke-git/lancet/v2/mathutil" + "github.com/duke-git/lancet/v2/slice" "github.com/getlantern/systray" "github.com/wailsapp/wails/v2/pkg/runtime" "go-stock/backend/data" @@ -50,7 +51,7 @@ func (a *App) domReady(ctx context.Context) { // Add your action here //定时更新数据 go func() { - ticker := time.NewTicker(time.Second * 2) + ticker := time.NewTicker(time.Second * 1) defer ticker.Stop() for range ticker.C { if isTradingTime(time.Now()) { @@ -97,26 +98,64 @@ func MonitorStockPrices(a *App) { dest := &[]data.FollowedStock{} db.Dao.Model(&data.FollowedStock{}).Find(dest) total := float64(0) - for _, follow := range *dest { - stockData := getStockInfo(follow) - total += stockData.ProfitAmountToday - price, _ := convertor.ToFloat(stockData.Price) - if stockData.PrePrice != price { - go runtime.EventsEmit(a.ctx, "stock_price", stockData) + //for _, follow := range *dest { + // stockData := getStockInfo(follow) + // total += stockData.ProfitAmountToday + // price, _ := convertor.ToFloat(stockData.Price) + // if stockData.PrePrice != price { + // go runtime.EventsEmit(a.ctx, "stock_price", stockData) + // } + //} + + stockInfos := GetStockInfos(*dest...) + for _, stockInfo := range *stockInfos { + total += stockInfo.ProfitAmountToday + price, _ := convertor.ToFloat(stockInfo.Price) + if stockInfo.PrePrice != price { + go runtime.EventsEmit(a.ctx, "stock_price", stockInfo) } } + title := "go-stock " + time.Now().Format(time.DateTime) + fmt.Sprintf(" %.2f¥", total) runtime.WindowSetTitle(a.ctx, title) systray.SetTooltip(title) } -func getStockInfo(follow data.FollowedStock) *data.StockInfo { - stockCode := follow.StockCode - stockData, err := data.NewStockDataApi().GetStockCodeRealTimeData(stockCode) +func GetStockInfos(follows ...data.FollowedStock) *[]data.StockInfo { + stockCodes := make([]string, 0) + for _, follow := range follows { + stockCodes = append(stockCodes, follow.StockCode) + } + stockData, err := data.NewStockDataApi().GetStockCodeRealTimeData(stockCodes...) if err != nil { logger.SugaredLogger.Errorf("get stock code real time data error:%s", err.Error()) return nil } + stockInfos := make([]data.StockInfo, 0) + for _, info := range *stockData { + v, ok := slice.FindBy(follows, func(idx int, follow data.FollowedStock) bool { + return follow.StockCode == info.Code + }) + if ok { + addStockFollowData(v, &info) + stockInfos = append(stockInfos, info) + } + } + return &stockInfos +} +func getStockInfo(follow data.FollowedStock) *data.StockInfo { + stockCode := follow.StockCode + stockDatas, err := data.NewStockDataApi().GetStockCodeRealTimeData(stockCode) + if err != nil { + logger.SugaredLogger.Errorf("get stock code real time data error:%s", err.Error()) + return nil + } + stockData := (*stockDatas)[0] + addStockFollowData(follow, &stockData) + return &stockData +} + +func addStockFollowData(follow data.FollowedStock, stockData *data.StockInfo) { stockData.PrePrice = follow.Price //上次当前价格 stockData.Sort = follow.Sort stockData.CostPrice = follow.CostPrice //成本价 @@ -164,11 +203,10 @@ func getStockInfo(follow data.FollowedStock) *data.StockInfo { //logger.SugaredLogger.Debugf("stockData:%+v", stockData) if follow.Price != price { - go db.Dao.Model(follow).Where("stock_code = ?", stockCode).Updates(map[string]interface{}{ + go db.Dao.Model(follow).Where("stock_code = ?", follow.StockCode).Updates(map[string]interface{}{ "price": stockData.Price, }) } - return stockData } // beforeClose is called when the application is about to quit, diff --git a/backend/data/stock_data_api.go b/backend/data/stock_data_api.go index 4212cb5..793531b 100644 --- a/backend/data/stock_data_api.go +++ b/backend/data/stock_data_api.go @@ -273,17 +273,29 @@ func (receiver StockDataApi) GetStockBaseInfo() { } -func (receiver StockDataApi) GetStockCodeRealTimeData(StockCode string) (*StockInfo, error) { +func (receiver StockDataApi) GetStockCodeRealTimeData(StockCodes ...string) (*[]StockInfo, error) { resp, err := receiver.client.R(). SetHeader("Host", "hq.sinajs.cn"). SetHeader("Referer", "https://finance.sina.com.cn/"). SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0"). - Get(fmt.Sprintf(sina_stook_url, time.Now().Unix(), StockCode)) + Get(fmt.Sprintf(sina_stook_url, time.Now().Unix(), slice.Join(StockCodes, ","))) if err != nil { logger.SugaredLogger.Error(err.Error()) - return &StockInfo{}, nil + return &[]StockInfo{}, nil + } + + stockInfos := make([]StockInfo, 0) + str := GB18030ToUTF8(resp.Body()) + dataStr := strutil.SplitEx(str, "\n", true) + for _, data := range dataStr { + //logger.SugaredLogger.Info(data) + stockData, err := ParseFullSingleStockData(data) + if err != nil { + logger.SugaredLogger.Error(err.Error()) + continue + } + stockInfos = append(stockInfos, *stockData) } - stockData, err := ParseFullSingleStockData(GB18030ToUTF8(resp.Body())) //var count int64 //db.Dao.Model(&StockInfo{}).Where("code = ?", StockCode).Count(&count) //if count == 0 { @@ -291,16 +303,17 @@ func (receiver StockDataApi) GetStockCodeRealTimeData(StockCode string) (*StockI //} else { // go db.Dao.Model(&StockInfo{}).Where("code = ?", StockCode).Updates(stockData) //} - return stockData, err + return &stockInfos, err } func (receiver StockDataApi) Follow(stockCode string) string { logger.SugaredLogger.Infof("Follow %s", stockCode) - stockInfo, err := receiver.GetStockCodeRealTimeData(stockCode) + stockInfos, err := receiver.GetStockCodeRealTimeData(stockCode) if err != nil { logger.SugaredLogger.Error(err.Error()) return "关注失败" } + stockInfo := (*stockInfos)[0] price, _ := convertor.ToFloat(stockInfo.Price) db.Dao.Model(&FollowedStock{}).FirstOrCreate(&FollowedStock{ StockCode: stockCode, diff --git a/backend/data/stock_data_api_test.go b/backend/data/stock_data_api_test.go index 6d5ca87..e1fc41f 100644 --- a/backend/data/stock_data_api_test.go +++ b/backend/data/stock_data_api_test.go @@ -2,11 +2,16 @@ package data import ( "encoding/json" + "fmt" "github.com/duke-git/lancet/v2/convertor" + "github.com/duke-git/lancet/v2/strutil" + "github.com/go-resty/resty/v2" "go-stock/backend/db" + "go-stock/backend/logger" "io/ioutil" "strings" "testing" + "time" ) // @Author spark @@ -14,9 +19,28 @@ import ( // @Desc //----------------------------------------------------------------------------------- +func TestParseFullSingleStockData(t *testing.T) { + resp, err := resty.New().R(). + SetHeader("Host", "hq.sinajs.cn"). + SetHeader("Referer", "https://finance.sina.com.cn/"). + SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0"). + Get(fmt.Sprintf(sina_stook_url, time.Now().Unix(), "sh600859,sh600745")) + if err != nil { + logger.SugaredLogger.Error(err.Error()) + } + data := GB18030ToUTF8(resp.Body()) + strs := strutil.SplitEx(data, "\n", true) + for _, str := range strs { + logger.SugaredLogger.Info(str) + } +} + func TestNewStockDataApi(t *testing.T) { stockDataApi := NewStockDataApi() - t.Log(stockDataApi.GetStockCodeRealTimeData("sh600859")) + datas, _ := stockDataApi.GetStockCodeRealTimeData("sh600859", "sh600745") + for _, data := range *datas { + t.Log(data) + } } func TestGetStockBaseInfo(t *testing.T) { @@ -74,7 +98,7 @@ func TestReadFile(t *testing.T) { func TestFollowedList(t *testing.T) { db.Init("../../data/stock.db") stockDataApi := NewStockDataApi() - t.Log(stockDataApi.GetFollowList()) + stockDataApi.GetFollowList() } diff --git a/frontend/src/components/stock.vue b/frontend/src/components/stock.vue index d3d1a50..7912b4b 100644 --- a/frontend/src/components/stock.vue +++ b/frontend/src/components/stock.vue @@ -47,7 +47,7 @@ const data = reactive({ }) const sortedResults = computed(() => { - console.log("computed",sortedResults.value) + //console.log("computed",sortedResults.value) const sortedKeys =Object.keys(results.value).sort(); const sortedObject = {}; sortedKeys.forEach(key => { @@ -104,7 +104,7 @@ EventsOn("showSearch",(data)=>{ }) EventsOn("stock_price",(data)=>{ - console.log("stock_price",data['股票代码']) + //console.log("stock_price",data['股票代码']) updateData(data) })