From 7dd10d443e4767e3da189acb61963591b4eeb3c1 Mon Sep 17 00:00:00 2001 From: ArvinLovegood Date: Wed, 25 Jun 2025 09:40:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(frontend):=E6=B7=BB=E5=8A=A0=E7=83=AD?= =?UTF-8?q?=E9=97=A8=E8=82=A1=E7=A5=A8=E3=80=81=E4=BA=8B=E4=BB=B6=E5=92=8C?= =?UTF-8?q?=E8=AF=9D=E9=A2=98=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 App.d.ts 和 App.js 中添加了 HotEvent、HotStock 和 HotTopic 函数 - 在 app_common.go 中实现了相关功能的后端逻辑 - 新增 HotEvents、HotStockList 和 HotTopics 组件用于前端展示 - 更新 market.vue以包含新的热门股票和话题功能 - 在 KLineChart.vue 中添加了代码和名称的显示 --- app_common.go | 17 ++++++ backend/data/market_news_api.go | 69 +++++++++++++++++++++ backend/data/market_news_api_test.go | 27 +++++++++ backend/models/models.go | 45 ++++++++++++++ frontend/src/components/HotEvents.vue | 37 ++++++++++++ frontend/src/components/HotStockList.vue | 76 ++++++++++++++++++++++++ frontend/src/components/HotTopics.vue | 43 ++++++++++++++ frontend/src/components/KLineChart.vue | 2 +- frontend/src/components/market.vue | 31 +++++++++- frontend/wailsjs/go/main/App.d.ts | 6 ++ frontend/wailsjs/go/main/App.js | 12 ++++ 11 files changed, 362 insertions(+), 3 deletions(-) create mode 100644 frontend/src/components/HotEvents.vue create mode 100644 frontend/src/components/HotStockList.vue create mode 100644 frontend/src/components/HotTopics.vue diff --git a/app_common.go b/app_common.go index a73a611..587be55 100644 --- a/app_common.go +++ b/app_common.go @@ -31,3 +31,20 @@ func (a App) EMDictCode(code string) []any { func (a App) AnalyzeSentiment(text string) data.SentimentResult { return data.AnalyzeSentiment(text) } + +func (a App) HotStock(marketType string) *[]models.HotItem { + return data.NewMarketNewsApi().XUEQIUHotStock(50, marketType) +} + +func (a App) HotEvent(size int) *[]models.HotEvent { + if size <= 0 { + size = 10 + } + return data.NewMarketNewsApi().HotEvent(size) +} +func (a App) HotTopic(size int) []any { + if size <= 0 { + size = 10 + } + return data.NewMarketNewsApi().HotTopic(size) +} diff --git a/backend/data/market_news_api.go b/backend/data/market_news_api.go index d1e5938..ff74b2a 100644 --- a/backend/data/market_news_api.go +++ b/backend/data/market_news_api.go @@ -574,3 +574,72 @@ func (m MarketNewsApi) TradingViewNews() *[]models.TVNews { json.Unmarshal(items, TVNews) return TVNews } + +func (m MarketNewsApi) XUEQIUHotStock(size int, marketType string) *[]models.HotItem { + url := fmt.Sprintf("https://stock.xueqiu.com/v5/stock/hot_stock/list.json?page=1&size=%d&_type=%s&type=%s", size, marketType, marketType) + res := &models.XUEQIUHot{} + _, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R(). + SetHeader("Host", "stock.xueqiu.com"). + SetHeader("Origin", "https://xueqiu.com"). + SetHeader("Referer", "https://xueqiu.com/"). + SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0"). + SetHeader("Cookie", "cookiesu=2617378771242871; s=c2121pp1u71; device_id=237a58584ec58d8e4d4e1040700a644f1; Hm_lvt_1db88642e346389874251b5a1eded6e3=1744100219,1744599115; xq_a_token=b7259d09435458cc3f1a963479abb270a1a016ce; xqat=b7259d09435458cc3f1a963479abb270a1a016ce; xq_r_token=28108bfa1d92ac8a46bbb57722633746218621a3; xq_id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1aWQiOi0xLCJpc3MiOiJ1YyIsImV4cCI6MTc1MjU0MTk4OCwiY3RtIjoxNzUwMjMwNjA2NzI0LCJjaWQiOiJkOWQwbjRBWnVwIn0.kU_fz0luJoE7nr-K4UrNUi5-mAG-vMdXtuC4mUKIppILId4UpF70LB70yunxGiNSw6tPFR3-hyLvztKAHtekCUTm3XjUl5b3tEDP-ZUVqHnWXO_5hoeMI8h-Cfx6ZGlIr5x3icvTPkT0OV5CD5A33-ZDTKhKPf-DhJ_-m7CG5GbX4MseOBeMXuLUQUiYHPKhX1QUc0GTGrCzi8Mki0z49D0LVqCSgbsx3UGfowOOyx85_cXb4OAFvIjwbs2p0o_h-ibIT0ngVkkAyEDetVvlcZ_bkardhseCB7k9BEMgH2z8ihgkVxyy3P0degLmDUruhmqn5uZOCi1pVBDvCv9lBg; u=261737877124287; ssxmod_itna=QuG=D5AKiKDIqCqGKi7G7DgmmPlSDWFqKGHDyx4YK0CDmxjKiddDUQivnb8xpnQcGyGYoYhoqEeDBubrDSxD67DK4GTm+ogiw1o3B=xedQHDgBtN=7/i1K53N+rOjquLMU=kbqYxB3DExGkqj0tPi4DxaPD5xDTDWeDGDD3DnnsDQKDRx0kL0oDIxD1D0bmHUEvh38mDYePLmOmDYPYx94Y8KoDeEgsD7HUl/vIGGEAqjLPFegXLD0HolCqr4DCid1qDm+ECfkjDn9sD0KP8fn+CRoDv=tYr4ibx+o=W+8vstf9mjGe3cXseWdBmoFrmf4DA3bFAxnAxD7vYxADaDoerDGHPoxHF+PKGPtDKmiqQGeB5qbi4eg4KDHKDe3DeG0qeEP9xVUoHDDWMYYM0ICr4FBimBDM7D0x4QOECmhul5QCN/m5/74lGm=7x9Wp7A+i7xQ7wlMD4D; ssxmod_itna2=QuG=D5AKiKDIqCqGKi7G7DgmmPlSDWFqKGHDyx4YK0CDmxjKiddDUQivnb8xpnQcGyGYoYhoqoDirSDhPmGD24GajjDuGE3m7or4DlxOSGewHl6iaus2Q62SRX5CFjCds6ltF9xy6iaUuB262UkhRA8UXST=4/b+y3kGKzlGE8T29FA008ljy9jXXC7f7m7QsK667mlUooWrofk=qGZjxtcUrN1NtuAnne1hj+rQP5UnlFkxf+o7VjmatH7u7bCDlbTt3cz6CH9Fl4vye16W/ellc8I3Q37W7ZwiLGD/zPpZcnd2nsqqo/+zRbKAmz4plzwaDqGUe7f9E+P0IFRKqpRv+buQFHBSpcbwND7Q+9XWmnjI2UwKd98jIS3gPXwxvbx4OuiyH8gZ+OEt7DgE/AY/9W4VxDZrlFWyWnC4y4/I0IpAfaGKpbPmauKbkqawqv93vSf+9HamGe0Dt2PNgT3yiEB4vQP2/DdVpcGBOjFujWoHP32OshLPYI20LRCKddwEGkKqPzPwKPc3X5zuB=w2fUdtwKsAW5kQtsl8clNwjC5uDYrxR0h9xaj0xmD+YuI3GPT7xYTalRImPj2wL2=+91a304xa4bTWtP=dLGARhb/efRi0uktaz8i8C04G0x/ZWUzqRza8GGU=FfRfvb4GZM/q2rVsl0nLvRjGeAKgocLouyXs/uwZu3YxbAx30qCbjG1A533zAxIeIgD=0VAc3ixD"). + SetResult(res). + Get(url) + if err != nil { + logger.SugaredLogger.Errorf("XUEQIUHotStock err:%s", err.Error()) + return &[]models.HotItem{} + } + logger.SugaredLogger.Infof("XUEQIUHotStock:%+v", res) + return &res.Data.Items +} + +func (m MarketNewsApi) HotEvent(size int) *[]models.HotEvent { + events := &[]models.HotEvent{} + url := fmt.Sprintf("https://xueqiu.com/hot_event/list.json?count=%d", size) + resp, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R(). + SetHeader("Host", "xueqiu.com"). + SetHeader("Origin", "https://xueqiu.com"). + SetHeader("Referer", "https://xueqiu.com/"). + SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0"). + SetHeader("Cookie", "cookiesu=2617378771242871; s=c2121pp1u71; device_id=237a58584ec58d8e4d4e1040700a644f1; Hm_lvt_1db88642e346389874251b5a1eded6e3=1744100219,1744599115; xq_a_token=b7259d09435458cc3f1a963479abb270a1a016ce; xqat=b7259d09435458cc3f1a963479abb270a1a016ce; xq_r_token=28108bfa1d92ac8a46bbb57722633746218621a3; xq_id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1aWQiOi0xLCJpc3MiOiJ1YyIsImV4cCI6MTc1MjU0MTk4OCwiY3RtIjoxNzUwMjMwNjA2NzI0LCJjaWQiOiJkOWQwbjRBWnVwIn0.kU_fz0luJoE7nr-K4UrNUi5-mAG-vMdXtuC4mUKIppILId4UpF70LB70yunxGiNSw6tPFR3-hyLvztKAHtekCUTm3XjUl5b3tEDP-ZUVqHnWXO_5hoeMI8h-Cfx6ZGlIr5x3icvTPkT0OV5CD5A33-ZDTKhKPf-DhJ_-m7CG5GbX4MseOBeMXuLUQUiYHPKhX1QUc0GTGrCzi8Mki0z49D0LVqCSgbsx3UGfowOOyx85_cXb4OAFvIjwbs2p0o_h-ibIT0ngVkkAyEDetVvlcZ_bkardhseCB7k9BEMgH2z8ihgkVxyy3P0degLmDUruhmqn5uZOCi1pVBDvCv9lBg; u=261737877124287; ssxmod_itna=QuG=D5AKiKDIqCqGKi7G7DgmmPlSDWFqKGHDyx4YK0CDmxjKiddDUQivnb8xpnQcGyGYoYhoqEeDBubrDSxD67DK4GTm+ogiw1o3B=xedQHDgBtN=7/i1K53N+rOjquLMU=kbqYxB3DExGkqj0tPi4DxaPD5xDTDWeDGDD3DnnsDQKDRx0kL0oDIxD1D0bmHUEvh38mDYePLmOmDYPYx94Y8KoDeEgsD7HUl/vIGGEAqjLPFegXLD0HolCqr4DCid1qDm+ECfkjDn9sD0KP8fn+CRoDv=tYr4ibx+o=W+8vstf9mjGe3cXseWdBmoFrmf4DA3bFAxnAxD7vYxADaDoerDGHPoxHF+PKGPtDKmiqQGeB5qbi4eg4KDHKDe3DeG0qeEP9xVUoHDDWMYYM0ICr4FBimBDM7D0x4QOECmhul5QCN/m5/74lGm=7x9Wp7A+i7xQ7wlMD4D; ssxmod_itna2=QuG=D5AKiKDIqCqGKi7G7DgmmPlSDWFqKGHDyx4YK0CDmxjKiddDUQivnb8xpnQcGyGYoYhoqoDirSDhPmGD24GajjDuGE3m7or4DlxOSGewHl6iaus2Q62SRX5CFjCds6ltF9xy6iaUuB262UkhRA8UXST=4/b+y3kGKzlGE8T29FA008ljy9jXXC7f7m7QsK667mlUooWrofk=qGZjxtcUrN1NtuAnne1hj+rQP5UnlFkxf+o7VjmatH7u7bCDlbTt3cz6CH9Fl4vye16W/ellc8I3Q37W7ZwiLGD/zPpZcnd2nsqqo/+zRbKAmz4plzwaDqGUe7f9E+P0IFRKqpRv+buQFHBSpcbwND7Q+9XWmnjI2UwKd98jIS3gPXwxvbx4OuiyH8gZ+OEt7DgE/AY/9W4VxDZrlFWyWnC4y4/I0IpAfaGKpbPmauKbkqawqv93vSf+9HamGe0Dt2PNgT3yiEB4vQP2/DdVpcGBOjFujWoHP32OshLPYI20LRCKddwEGkKqPzPwKPc3X5zuB=w2fUdtwKsAW5kQtsl8clNwjC5uDYrxR0h9xaj0xmD+YuI3GPT7xYTalRImPj2wL2=+91a304xa4bTWtP=dLGARhb/efRi0uktaz8i8C04G0x/ZWUzqRza8GGU=FfRfvb4GZM/q2rVsl0nLvRjGeAKgocLouyXs/uwZu3YxbAx30qCbjG1A533zAxIeIgD=0VAc3ixD"). + Get(url) + if err != nil { + logger.SugaredLogger.Errorf("HotEvent err:%s", err.Error()) + return events + } + logger.SugaredLogger.Infof("HotEvent:%s", resp.Body()) + respMap := map[string]any{} + err = json.Unmarshal(resp.Body(), &respMap) + items, err := json.Marshal(respMap["list"]) + if err != nil { + return events + } + json.Unmarshal(items, events) + return events + +} + +func (m MarketNewsApi) HotTopic(size int) []any { + url := "https://gubatopic.eastmoney.com/interface/GetData.aspx?path=newtopic/api/Topic/HomePageListRead" + resp, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R(). + SetHeader("Host", "gubatopic.eastmoney.com"). + SetHeader("Origin", "https://gubatopic.eastmoney.com"). + SetHeader("Referer", "https://gubatopic.eastmoney.com/"). + SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0"). + SetFormData(map[string]string{ + "param": fmt.Sprintf("ps=%d&p=1&type=0", size), + "path": "newtopic/api/Topic/HomePageListRead", + "env": "2", + }). + Post(url) + if err != nil { + logger.SugaredLogger.Errorf("HotTopic err:%s", err.Error()) + return []any{} + } + logger.SugaredLogger.Infof("HotTopic:%s", resp.Body()) + respMap := map[string]any{} + err = json.Unmarshal(resp.Body(), &respMap) + return respMap["re"].([]any) + +} diff --git a/backend/data/market_news_api_test.go b/backend/data/market_news_api_test.go index 01d489b..d6279bd 100644 --- a/backend/data/market_news_api_test.go +++ b/backend/data/market_news_api_test.go @@ -108,3 +108,30 @@ func TestTradingViewNews(t *testing.T) { logger.SugaredLogger.Debugf("value: %+v", a) } } + +func TestXUEQIUHotStock(t *testing.T) { + db.Init("../../data/stock.db") + res := NewMarketNewsApi().XUEQIUHotStock(50, "10") + for _, a := range *res { + logger.SugaredLogger.Debugf("value: %+v", a) + } + +} + +func TestHotEvent(t *testing.T) { + db.Init("../../data/stock.db") + res := NewMarketNewsApi().HotEvent(50) + for _, a := range *res { + logger.SugaredLogger.Debugf("value: %+v", a) + } + +} + +func TestHotTopic(t *testing.T) { + db.Init("../../data/stock.db") + res := NewMarketNewsApi().HotTopic(10) + for _, a := range res { + logger.SugaredLogger.Debugf("value: %+v", a) + } + +} diff --git a/backend/models/models.go b/backend/models/models.go index f513055..10d237b 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -317,3 +317,48 @@ type TVNews struct { LogoId string `json:"logo_id"` } `json:"provider"` } + +type XUEQIUHot struct { + Data struct { + Items []HotItem `json:"items"` + ItemsSize int `json:"items_size"` + } `json:"data"` + ErrorCode int `json:"error_code"` + ErrorDescription string `json:"error_description"` +} + +type HotItem struct { + Type int `json:"type"` + Code string `json:"code"` + Name string `json:"name"` + Value float64 `json:"value"` + Increment int `json:"increment"` + RankChange int `json:"rank_change"` + HasExist interface{} `json:"has_exist"` + Symbol string `json:"symbol"` + Percent float64 `json:"percent"` + Current float64 `json:"current"` + Chg float64 `json:"chg"` + Exchange string `json:"exchange"` + StockType int `json:"stock_type"` + SubType string `json:"sub_type"` + Ad int `json:"ad"` + AdId interface{} `json:"ad_id"` + ContentId interface{} `json:"content_id"` + Page interface{} `json:"page"` + Model interface{} `json:"model"` + Location interface{} `json:"location"` + TradeSession interface{} `json:"trade_session"` + CurrentExt interface{} `json:"current_ext"` + PercentExt interface{} `json:"percent_ext"` +} + +type HotEvent struct { + PicSize interface{} `json:"pic_size"` + Tag string `json:"tag"` + Id int `json:"id"` + Pic string `json:"pic"` + Hot int `json:"hot"` + StatusCount int `json:"status_count"` + Content string `json:"content"` +} diff --git a/frontend/src/components/HotEvents.vue b/frontend/src/components/HotEvents.vue new file mode 100644 index 0000000..8571f3f --- /dev/null +++ b/frontend/src/components/HotEvents.vue @@ -0,0 +1,37 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/HotStockList.vue b/frontend/src/components/HotStockList.vue new file mode 100644 index 0000000..5f978c5 --- /dev/null +++ b/frontend/src/components/HotStockList.vue @@ -0,0 +1,76 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/HotTopics.vue b/frontend/src/components/HotTopics.vue new file mode 100644 index 0000000..6a3850f --- /dev/null +++ b/frontend/src/components/HotTopics.vue @@ -0,0 +1,43 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/KLineChart.vue b/frontend/src/components/KLineChart.vue index 5791430..e1889c1 100644 --- a/frontend/src/components/KLineChart.vue +++ b/frontend/src/components/KLineChart.vue @@ -60,7 +60,7 @@ function handleKLine(code,name){ ////console.log("values",values) let option = { title: { - text: name, + text: name+" "+code, left: '20px', textStyle: { color: darkTheme?'#ccc':'#456' diff --git a/frontend/src/components/market.vue b/frontend/src/components/market.vue index 7944673..c41038e 100644 --- a/frontend/src/components/market.vue +++ b/frontend/src/components/market.vue @@ -26,6 +26,9 @@ import StockResearchReportList from "./StockResearchReportList.vue"; import StockNoticeList from "./StockNoticeList.vue"; import LongTigerRankList from "./LongTigerRankList.vue"; import IndustryResearchReportList from "./IndustryResearchReportList.vue"; +import HotStockList from "./HotStockList.vue"; +import HotEvents from "./HotEvents.vue"; +import HotTopics from "./HotTopics.vue"; const route = useRoute() const icon = ref('https://raw.githubusercontent.com/ArvinLovegood/go-stock/master/build/appicon.png'); @@ -557,8 +560,32 @@ function ReFlesh(source) { - - + + + + + + + + + + + + + + + + + + + + + + + + + + >; export function Greet(arg1:string):Promise; +export function HotEvent(arg1:number):Promise; + +export function HotStock(arg1:string):Promise; + +export function HotTopic(arg1:number):Promise>; + export function IndustryResearchReport(arg1:string):Promise>; export function LongTigerRank(arg1:string):Promise; diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js index 27d7441..0e93c54 100644 --- a/frontend/wailsjs/go/main/App.js +++ b/frontend/wailsjs/go/main/App.js @@ -126,6 +126,18 @@ export function Greet(arg1) { return window['go']['main']['App']['Greet'](arg1); } +export function HotEvent(arg1) { + return window['go']['main']['App']['HotEvent'](arg1); +} + +export function HotStock(arg1) { + return window['go']['main']['App']['HotStock'](arg1); +} + +export function HotTopic(arg1) { + return window['go']['main']['App']['HotTopic'](arg1); +} + export function IndustryResearchReport(arg1) { return window['go']['main']['App']['IndustryResearchReport'](arg1); }