feat(backend): 优化股票数据获取逻辑

- 修改 GetStockCodeRealTimeData 方法,支持批量获取多个股票代码的实时数据
- 新增 GetStockInfos 函数,用于获取关注股票的实时信息- 重构 getStockInfo 函数,提高代码复用性
- 优化数据处理逻辑,提高程序运行效率
This commit is contained in:
spark 2025-01-09 14:45:25 +08:00
parent ad428f83f8
commit 7e27996f17
4 changed files with 97 additions and 22 deletions

62
app.go
View File

@ -8,6 +8,7 @@ import (
"github.com/coocood/freecache" "github.com/coocood/freecache"
"github.com/duke-git/lancet/v2/convertor" "github.com/duke-git/lancet/v2/convertor"
"github.com/duke-git/lancet/v2/mathutil" "github.com/duke-git/lancet/v2/mathutil"
"github.com/duke-git/lancet/v2/slice"
"github.com/getlantern/systray" "github.com/getlantern/systray"
"github.com/wailsapp/wails/v2/pkg/runtime" "github.com/wailsapp/wails/v2/pkg/runtime"
"go-stock/backend/data" "go-stock/backend/data"
@ -50,7 +51,7 @@ func (a *App) domReady(ctx context.Context) {
// Add your action here // Add your action here
//定时更新数据 //定时更新数据
go func() { go func() {
ticker := time.NewTicker(time.Second * 2) ticker := time.NewTicker(time.Second * 1)
defer ticker.Stop() defer ticker.Stop()
for range ticker.C { for range ticker.C {
if isTradingTime(time.Now()) { if isTradingTime(time.Now()) {
@ -97,26 +98,64 @@ func MonitorStockPrices(a *App) {
dest := &[]data.FollowedStock{} dest := &[]data.FollowedStock{}
db.Dao.Model(&data.FollowedStock{}).Find(dest) db.Dao.Model(&data.FollowedStock{}).Find(dest)
total := float64(0) total := float64(0)
for _, follow := range *dest { //for _, follow := range *dest {
stockData := getStockInfo(follow) // stockData := getStockInfo(follow)
total += stockData.ProfitAmountToday // total += stockData.ProfitAmountToday
price, _ := convertor.ToFloat(stockData.Price) // price, _ := convertor.ToFloat(stockData.Price)
if stockData.PrePrice != price { // if stockData.PrePrice != price {
go runtime.EventsEmit(a.ctx, "stock_price", stockData) // 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) title := "go-stock " + time.Now().Format(time.DateTime) + fmt.Sprintf(" %.2f¥", total)
runtime.WindowSetTitle(a.ctx, title) runtime.WindowSetTitle(a.ctx, title)
systray.SetTooltip(title) systray.SetTooltip(title)
} }
func getStockInfo(follow data.FollowedStock) *data.StockInfo { func GetStockInfos(follows ...data.FollowedStock) *[]data.StockInfo {
stockCode := follow.StockCode stockCodes := make([]string, 0)
stockData, err := data.NewStockDataApi().GetStockCodeRealTimeData(stockCode) for _, follow := range follows {
stockCodes = append(stockCodes, follow.StockCode)
}
stockData, err := data.NewStockDataApi().GetStockCodeRealTimeData(stockCodes...)
if err != nil { if err != nil {
logger.SugaredLogger.Errorf("get stock code real time data error:%s", err.Error()) logger.SugaredLogger.Errorf("get stock code real time data error:%s", err.Error())
return nil 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.PrePrice = follow.Price //上次当前价格
stockData.Sort = follow.Sort stockData.Sort = follow.Sort
stockData.CostPrice = follow.CostPrice //成本价 stockData.CostPrice = follow.CostPrice //成本价
@ -164,11 +203,10 @@ func getStockInfo(follow data.FollowedStock) *data.StockInfo {
//logger.SugaredLogger.Debugf("stockData:%+v", stockData) //logger.SugaredLogger.Debugf("stockData:%+v", stockData)
if follow.Price != price { 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, "price": stockData.Price,
}) })
} }
return stockData
} }
// beforeClose is called when the application is about to quit, // beforeClose is called when the application is about to quit,

View File

@ -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(). resp, err := receiver.client.R().
SetHeader("Host", "hq.sinajs.cn"). SetHeader("Host", "hq.sinajs.cn").
SetHeader("Referer", "https://finance.sina.com.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"). 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 { if err != nil {
logger.SugaredLogger.Error(err.Error()) 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 //var count int64
//db.Dao.Model(&StockInfo{}).Where("code = ?", StockCode).Count(&count) //db.Dao.Model(&StockInfo{}).Where("code = ?", StockCode).Count(&count)
//if count == 0 { //if count == 0 {
@ -291,16 +303,17 @@ func (receiver StockDataApi) GetStockCodeRealTimeData(StockCode string) (*StockI
//} else { //} else {
// go db.Dao.Model(&StockInfo{}).Where("code = ?", StockCode).Updates(stockData) // go db.Dao.Model(&StockInfo{}).Where("code = ?", StockCode).Updates(stockData)
//} //}
return stockData, err return &stockInfos, err
} }
func (receiver StockDataApi) Follow(stockCode string) string { func (receiver StockDataApi) Follow(stockCode string) string {
logger.SugaredLogger.Infof("Follow %s", stockCode) logger.SugaredLogger.Infof("Follow %s", stockCode)
stockInfo, err := receiver.GetStockCodeRealTimeData(stockCode) stockInfos, err := receiver.GetStockCodeRealTimeData(stockCode)
if err != nil { if err != nil {
logger.SugaredLogger.Error(err.Error()) logger.SugaredLogger.Error(err.Error())
return "关注失败" return "关注失败"
} }
stockInfo := (*stockInfos)[0]
price, _ := convertor.ToFloat(stockInfo.Price) price, _ := convertor.ToFloat(stockInfo.Price)
db.Dao.Model(&FollowedStock{}).FirstOrCreate(&FollowedStock{ db.Dao.Model(&FollowedStock{}).FirstOrCreate(&FollowedStock{
StockCode: stockCode, StockCode: stockCode,

View File

@ -2,11 +2,16 @@ package data
import ( import (
"encoding/json" "encoding/json"
"fmt"
"github.com/duke-git/lancet/v2/convertor" "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/db"
"go-stock/backend/logger"
"io/ioutil" "io/ioutil"
"strings" "strings"
"testing" "testing"
"time"
) )
// @Author spark // @Author spark
@ -14,9 +19,28 @@ import (
// @Desc // @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) { func TestNewStockDataApi(t *testing.T) {
stockDataApi := NewStockDataApi() stockDataApi := NewStockDataApi()
t.Log(stockDataApi.GetStockCodeRealTimeData("sh600859")) datas, _ := stockDataApi.GetStockCodeRealTimeData("sh600859", "sh600745")
for _, data := range *datas {
t.Log(data)
}
} }
func TestGetStockBaseInfo(t *testing.T) { func TestGetStockBaseInfo(t *testing.T) {
@ -74,7 +98,7 @@ func TestReadFile(t *testing.T) {
func TestFollowedList(t *testing.T) { func TestFollowedList(t *testing.T) {
db.Init("../../data/stock.db") db.Init("../../data/stock.db")
stockDataApi := NewStockDataApi() stockDataApi := NewStockDataApi()
t.Log(stockDataApi.GetFollowList()) stockDataApi.GetFollowList()
} }

View File

@ -47,7 +47,7 @@ const data = reactive({
}) })
const sortedResults = computed(() => { const sortedResults = computed(() => {
console.log("computed",sortedResults.value) //console.log("computed",sortedResults.value)
const sortedKeys =Object.keys(results.value).sort(); const sortedKeys =Object.keys(results.value).sort();
const sortedObject = {}; const sortedObject = {};
sortedKeys.forEach(key => { sortedKeys.forEach(key => {
@ -104,7 +104,7 @@ EventsOn("showSearch",(data)=>{
}) })
EventsOn("stock_price",(data)=>{ EventsOn("stock_price",(data)=>{
console.log("stock_price",data['股票代码']) //console.log("stock_price",data[''])
updateData(data) updateData(data)
}) })