feat(frontend):添加市场资讯功能

- 新增市场资讯页面,用于展示财经新闻
- 实现电报列表获取和实时更新功能
- 添加新闻标签和股票标签显示
- 优化新闻列表展示样式
This commit is contained in:
ArvinLovegood 2025-04-23 16:39:54 +08:00
parent 3a74e0ed98
commit 6be23d6abc
11 changed files with 272 additions and 17 deletions

14
app.go
View File

@ -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
}

View File

@ -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
}

View File

@ -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().

View File

@ -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) {

View File

@ -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"
}

View File

@ -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"
>
<n-flex justify="center">
<n-flex>
<n-grid x-gap="12" :cols="1">
<!--
<n-gi style="position: relative;top:1px;z-index: 19;width: 100%" v-if="telegraph.length>0">

View File

@ -0,0 +1,71 @@
<script setup>
import {computed, h, onBeforeMount, onBeforeUnmount, onMounted, reactive, ref} from 'vue'
import {
NFlex,
NTimeline,
NTimelineItem,
} from 'naive-ui'
import * as echarts from 'echarts';
import {GetTelegraphList} from "../../wailsjs/go/main/App";
import {EventsOn} from "../../wailsjs/runtime";
const telegraphList= ref([])
onBeforeMount(() => {
GetTelegraphList().then((res) => {
telegraphList.value = res
})
})
EventsOn("newTelegraph", (data) => {
for (let i = 0; i < data.length; i++) {
telegraphList.value.pop()
}
telegraphList.value.unshift(...data)
})
</script>
<template>
<n-grid :cols="2" :y-gap="6">
<!-- <n-gi>-->
<!-- <n-card title="上证指数">-->
<!-- 卡片内容-->
<!-- </n-card>-->
<!-- </n-gi>-->
<!-- <n-gi>-->
<!-- <n-card title="深证成指">-->
<!-- 卡片内容-->
<!-- </n-card>-->
<!-- </n-gi>-->
<n-gi span="2">
<n-flex justify="flex-start">
<n-list bordered>
<template #header>
财联社电报
</template>
<n-list-item v-for="item in telegraphList" >
<n-space justify="start">
<n-text :bordered="false" :type="item.isRed?'error':'info'"> <n-tag size="small" :type="item.isRed?'error':'warning'" :bordered="false"> {{item.time}}</n-tag>{{item.content}}</n-text>
</n-space>
<n-space v-if="item.subjects" style="margin-top: 2px">
<n-tag :bordered="false" type="success" size="small" v-for="sub in item.subjects">
{{sub}}
</n-tag>
<n-space v-if="item.stocks">
<n-tag :bordered="false" type="warning" size="small" v-for="sub in item.stocks">
{{sub}}
</n-tag>
</n-space>
<n-tag v-if="item.url" :bordered="false" type="warning" size="small" >
<a :href="item.url" target="_blank" ><n-text type="warning">查看原文</n-text></a>
</n-tag>
</n-space>
</n-list-item>
</n-list>
</n-flex>
</n-gi>
</n-grid>
</template>
<style scoped>
</style>

View File

@ -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({

View File

@ -39,6 +39,8 @@ export function GetStockKLine(arg1:string,arg2:string,arg3:number):Promise<any>;
export function GetStockList(arg1:string):Promise<Array<data.StockBasic>>;
export function GetTelegraphList():Promise<any>;
export function GetVersionInfo():Promise<models.VersionInfo>;
export function GetfundList(arg1:string):Promise<Array<data.FundBasic>>;

View File

@ -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']();
}

31
main.go
View File

@ -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()