From 6be23d6abcfa91b20b1b6d7837fab6bd76be9d6b Mon Sep 17 00:00:00 2001 From: ArvinLovegood Date: Wed, 23 Apr 2025 16:39:54 +0800 Subject: [PATCH] =?UTF-8?q?feat(frontend):=E6=B7=BB=E5=8A=A0=E5=B8=82?= =?UTF-8?q?=E5=9C=BA=E8=B5=84=E8=AE=AF=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增市场资讯页面,用于展示财经新闻 - 实现电报列表获取和实时更新功能 - 添加新闻标签和股票标签显示 - 优化新闻列表展示样式 --- app.go | 14 ++++++ backend/data/market_news_api.go | 36 +++++++++++++++ backend/data/openai_api.go | 64 ++++++++++++++++++++++++++ backend/data/stock_data_api_test.go | 11 ++++- backend/models/models.go | 34 ++++++++++++++ frontend/src/App.vue | 20 +++++++- frontend/src/components/market.vue | 71 +++++++++++++++++++++++++++++ frontend/src/router/router.js | 2 + frontend/wailsjs/go/main/App.d.ts | 2 + frontend/wailsjs/go/main/App.js | 4 ++ main.go | 31 +++++++------ 11 files changed, 272 insertions(+), 17 deletions(-) create mode 100644 backend/data/market_news_api.go create mode 100644 frontend/src/components/market.vue diff --git a/app.go b/app.go index 8d9ad03..fa5cf6c 100644 --- a/app.go +++ b/app.go @@ -160,6 +160,15 @@ func (a *App) domReady(ctx context.Context) { } else { a.cronEntrys["MonitorStockPrices"] = id } + entryID, err := a.cron.AddFunc(fmt.Sprintf("@every %ds", interval+10), func() { + news := data.GetNewTelegraph(30) + go runtime.EventsEmit(a.ctx, "newTelegraph", news) + }) + if err != nil { + logger.SugaredLogger.Errorf("AddFunc error:%s", err.Error()) + } else { + a.cronEntrys["GetNewTelegraph"] = entryID + } }() @@ -1047,3 +1056,8 @@ func (a *App) RemoveGroup(groupId int) string { func (a *App) GetStockKLine(stockCode, stockName string, days int64) *[]data.KLineData { return data.NewStockDataApi().GetHK_KLineData(stockCode, "day", days) } + +func (a *App) GetTelegraphList() *[]*models.Telegraph { + telegraphs := data.NewMarketNewsApi().GetTelegraphList() + return telegraphs +} diff --git a/backend/data/market_news_api.go b/backend/data/market_news_api.go new file mode 100644 index 0000000..5704c12 --- /dev/null +++ b/backend/data/market_news_api.go @@ -0,0 +1,36 @@ +package data + +import ( + "github.com/samber/lo" + "go-stock/backend/db" + "go-stock/backend/logger" + "go-stock/backend/models" +) + +// @Author spark +// @Date 2025/4/23 14:54 +// @Desc +// ----------------------------------------------------------------------------------- +type MarketNewsApi struct { +} + +func NewMarketNewsApi() *MarketNewsApi { + return &MarketNewsApi{} +} + +func (m MarketNewsApi) GetTelegraphList() *[]*models.Telegraph { + news := &[]*models.Telegraph{} + db.Dao.Model(news).Preload("TelegraphTags").Order("id desc").Limit(20).Find(news) + for _, item := range *news { + tags := &[]models.Tags{} + db.Dao.Model(&models.Tags{}).Where("id in ?", lo.Map(item.TelegraphTags, func(item models.TelegraphTags, index int) uint { + return item.TagId + })).Find(&tags) + tagNames := lo.Map(*tags, func(item models.Tags, index int) string { + return item.Name + }) + item.SubjectTags = tagNames + logger.SugaredLogger.Infof("tagNames %v ,SubjectTags:%s", tagNames, item.SubjectTags) + } + return news +} diff --git a/backend/data/openai_api.go b/backend/data/openai_api.go index 4dcf0ba..26e3ecc 100644 --- a/backend/data/openai_api.go +++ b/backend/data/openai_api.go @@ -716,6 +716,70 @@ func GetTelegraphList(crawlTimeOut int64) *[]string { return &telegraph } +func GetNewTelegraph(crawlTimeOut int64) *[]models.Telegraph { + url := "https://www.cls.cn/telegraph" + response, _ := resty.New().SetTimeout(time.Duration(crawlTimeOut)*time.Second).R(). + SetHeader("Referer", "https://www.cls.cn/"). + SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.60"). + Get(fmt.Sprintf(url)) + var telegraphs []models.Telegraph + //logger.SugaredLogger.Info(string(response.Body())) + document, _ := goquery.NewDocumentFromReader(strings.NewReader(string(response.Body()))) + + document.Find(".telegraph-list").Each(func(i int, selection *goquery.Selection) { + //logger.SugaredLogger.Info(selection.Text()) + telegraph := models.Telegraph{} + spans := selection.Find("div.telegraph-content-box span") + if spans.Length() == 2 { + telegraph.Time = spans.First().Text() + telegraph.Content = spans.Last().Text() + if spans.Last().HasClass("c-de0422") { + telegraph.IsRed = true + } + } + + labels := selection.Find("div a.label-item") + labels.Each(func(i int, selection *goquery.Selection) { + if selection.HasClass("link-label-item") { + telegraph.Url = selection.AttrOr("href", "") + } else { + tag := &models.Tags{ + Name: selection.Text(), + Type: "subject", + } + db.Dao.Model(tag).Where("name=? and type=?", selection.Text(), "subject").FirstOrCreate(&tag) + telegraph.SubjectTags = append(telegraph.SubjectTags, selection.Text()) + } + }) + stocks := selection.Find("div.telegraph-stock-plate-box a") + stocks.Each(func(i int, selection *goquery.Selection) { + telegraph.StocksTags = append(telegraph.StocksTags, selection.Text()) + }) + + //telegraph = append(telegraph, ReplaceSensitiveWords(selection.Text())) + if telegraph.Content != "" { + cnt := int64(0) + db.Dao.Model(telegraph).Where("time=?", telegraph.Time).Count(&cnt) + if cnt == 0 { + db.Dao.Create(&telegraph) + telegraphs = append(telegraphs, telegraph) + for _, tag := range telegraph.SubjectTags { + tagInfo := &models.Tags{} + db.Dao.Model(models.Tags{}).Where("name=? and type=?", tag, "subject").First(&tagInfo) + if tagInfo.ID > 0 { + db.Dao.Model(models.TelegraphTags{}).Where("telegraph_id=? and tag_id=?", telegraph.ID, tagInfo.ID).FirstOrCreate(&models.TelegraphTags{ + TelegraphId: telegraph.ID, + TagId: tagInfo.ID, + }) + } + } + } + + } + }) + return &telegraphs +} + func GetTopNewsList(crawlTimeOut int64) *[]string { url := "https://www.cls.cn" response, err := resty.New().SetTimeout(time.Duration(crawlTimeOut)*time.Second).R(). diff --git a/backend/data/stock_data_api_test.go b/backend/data/stock_data_api_test.go index 2adb0e8..95f99c5 100644 --- a/backend/data/stock_data_api_test.go +++ b/backend/data/stock_data_api_test.go @@ -22,7 +22,16 @@ import ( //----------------------------------------------------------------------------------- func TestGetTelegraph(t *testing.T) { - GetTelegraphList(30) + db.Init("../../data/stock.db") + + //telegraphs := GetTelegraphList(30) + //for _, telegraph := range *telegraphs { + // logger.SugaredLogger.Info(telegraph) + //} + list := GetNewTelegraph(30) + for _, telegraph := range *list { + logger.SugaredLogger.Infof("telegraph:%+v", telegraph) + } } func TestGetFinancialReports(t *testing.T) { diff --git a/backend/models/models.go b/backend/models/models.go index 0ac168a..6fc685f 100644 --- a/backend/models/models.go +++ b/backend/models/models.go @@ -215,3 +215,37 @@ type Prompt struct { Content string `json:"content"` Type string `json:"type"` } + +type Telegraph struct { + gorm.Model + Time string `json:"time"` + Content string `json:"content"` + SubjectTags []string `json:"subjects" gorm:"-:all"` + StocksTags []string `json:"stocks" gorm:"-:all"` + IsRed bool `json:"isRed"` + Url string `json:"url"` + TelegraphTags []TelegraphTags `json:"tags" gorm:"-:migration;foreignKey:TelegraphId"` +} +type TelegraphTags struct { + gorm.Model + TagId uint `json:"tagId"` + TelegraphId uint `json:"telegraphId"` +} + +func (t TelegraphTags) TableName() string { + return "telegraph_tags" +} + +type Tags struct { + gorm.Model + Name string `json:"name"` + Type string `json:"type"` +} + +func (p Tags) TableName() string { + return "tags" +} + +func (p Telegraph) TableName() string { + return "telegraph_list" +} diff --git a/frontend/src/App.vue b/frontend/src/App.vue index f213a05..15df4c4 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -15,7 +15,7 @@ import { SettingsOutline, ReorderTwoOutline, ExpandOutline, - PowerOutline, LogoGithub, MoveOutline, WalletOutline, StarOutline, AlarmOutline, SparklesOutline, + PowerOutline, LogoGithub, MoveOutline, WalletOutline, StarOutline, AlarmOutline, SparklesOutline, NewspaperOutline, } from '@vicons/ionicons5' import {GetConfig} from "../wailsjs/go/main/App"; const enableNews= ref(false) @@ -53,6 +53,22 @@ const menuOptions = ref([ }, ] }, + { + label: () => + h( + RouterLink, + { + to: { + name: 'market', + params: { + } + } + }, + { default: () => '市场资讯' } + ), + key: 'market', + icon: renderIcon(NewspaperOutline), + }, { label: () => h( @@ -246,7 +262,7 @@ onMounted(()=>{ :y-offset="150" :rotate="-15" > - + + + + + + + + + + + + + + + + + {{item.time}}{{item.content}} + + + + {{sub}} + + + + {{sub}} + + + + 查看原文 + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/router/router.js b/frontend/src/router/router.js index 8eba737..79f82ba 100644 --- a/frontend/src/router/router.js +++ b/frontend/src/router/router.js @@ -4,12 +4,14 @@ import stockView from '../components/stock.vue' import settingsView from '../components/settings.vue' import about from "../components/about.vue"; import fundView from "../components/fund.vue"; +import market from "../components/market.vue"; const routes = [ { path: '/', component: stockView,name: 'stock' }, { path: '/fund', component: fundView,name: 'fund' }, { path: '/settings', component: settingsView,name: 'settings' }, { path: '/about', component: about,name: 'about' }, + { path: '/market', component: market,name: 'market' }, ] const router = createRouter({ diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts index b19d1c6..dec5fd7 100644 --- a/frontend/wailsjs/go/main/App.d.ts +++ b/frontend/wailsjs/go/main/App.d.ts @@ -39,6 +39,8 @@ export function GetStockKLine(arg1:string,arg2:string,arg3:number):Promise; export function GetStockList(arg1:string):Promise>; +export function GetTelegraphList():Promise; + export function GetVersionInfo():Promise; export function GetfundList(arg1:string):Promise>; diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js index 1b619d1..a96de0d 100644 --- a/frontend/wailsjs/go/main/App.js +++ b/frontend/wailsjs/go/main/App.js @@ -74,6 +74,10 @@ export function GetStockList(arg1) { return window['go']['main']['App']['GetStockList'](arg1); } +export function GetTelegraphList() { + return window['go']['main']['App']['GetTelegraphList'](); +} + export function GetVersionInfo() { return window['go']['main']['App']['GetVersionInfo'](); } diff --git a/main.go b/main.go index 485d06f..59c2ff6 100644 --- a/main.go +++ b/main.go @@ -68,26 +68,29 @@ func main() { db.Dao.AutoMigrate(&models.PromptTemplate{}) db.Dao.AutoMigrate(&data.Group{}) db.Dao.AutoMigrate(&data.GroupStock{}) + db.Dao.AutoMigrate(&models.Tags{}) + db.Dao.AutoMigrate(&models.Telegraph{}) + db.Dao.AutoMigrate(&models.TelegraphTags{}) //db.Dao.Model(&data.Group{}).Where("id = ?", 0).FirstOrCreate(&data.Group{ // Name: "默认分组", // Sort: 0, //}) - if stocksBin != nil && len(stocksBin) > 0 { - go initStockData() - } - log.SugaredLogger.Infof("init stocksBinHK %d", len(stocksBinHK)) - - if stocksBinHK != nil && len(stocksBinHK) > 0 { - go initStockDataHK() - } - log.SugaredLogger.Infof("init stocksBinUS %d", len(stocksBinUS)) - - if stocksBinUS != nil && len(stocksBinUS) > 0 { - go initStockDataUS() - } - updateBasicInfo() + //if stocksBin != nil && len(stocksBin) > 0 { + // go initStockData() + //} + //log.SugaredLogger.Infof("init stocksBinHK %d", len(stocksBinHK)) + // + //if stocksBinHK != nil && len(stocksBinHK) > 0 { + // go initStockDataHK() + //} + //log.SugaredLogger.Infof("init stocksBinUS %d", len(stocksBinUS)) + // + //if stocksBinUS != nil && len(stocksBinUS) > 0 { + // go initStockDataUS() + //} + //updateBasicInfo() // Create an instance of the app structure app := NewApp()