mirror of
https://github.com/ArvinLovegood/go-stock.git
synced 2025-07-19 00:00:09 +08:00
feat(frontend):添加市场资讯功能
- 新增市场资讯页面,用于展示财经新闻 - 实现电报列表获取和实时更新功能 - 添加新闻标签和股票标签显示 - 优化新闻列表展示样式
This commit is contained in:
parent
3a74e0ed98
commit
6be23d6abc
14
app.go
14
app.go
@ -160,6 +160,15 @@ func (a *App) domReady(ctx context.Context) {
|
|||||||
} else {
|
} else {
|
||||||
a.cronEntrys["MonitorStockPrices"] = id
|
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 {
|
func (a *App) GetStockKLine(stockCode, stockName string, days int64) *[]data.KLineData {
|
||||||
return data.NewStockDataApi().GetHK_KLineData(stockCode, "day", days)
|
return data.NewStockDataApi().GetHK_KLineData(stockCode, "day", days)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) GetTelegraphList() *[]*models.Telegraph {
|
||||||
|
telegraphs := data.NewMarketNewsApi().GetTelegraphList()
|
||||||
|
return telegraphs
|
||||||
|
}
|
||||||
|
36
backend/data/market_news_api.go
Normal file
36
backend/data/market_news_api.go
Normal 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
|
||||||
|
}
|
@ -716,6 +716,70 @@ func GetTelegraphList(crawlTimeOut int64) *[]string {
|
|||||||
return &telegraph
|
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 {
|
func GetTopNewsList(crawlTimeOut int64) *[]string {
|
||||||
url := "https://www.cls.cn"
|
url := "https://www.cls.cn"
|
||||||
response, err := resty.New().SetTimeout(time.Duration(crawlTimeOut)*time.Second).R().
|
response, err := resty.New().SetTimeout(time.Duration(crawlTimeOut)*time.Second).R().
|
||||||
|
@ -22,7 +22,16 @@ import (
|
|||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
func TestGetTelegraph(t *testing.T) {
|
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) {
|
func TestGetFinancialReports(t *testing.T) {
|
||||||
|
@ -215,3 +215,37 @@ type Prompt struct {
|
|||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
Type string `json:"type"`
|
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"
|
||||||
|
}
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
SettingsOutline,
|
SettingsOutline,
|
||||||
ReorderTwoOutline,
|
ReorderTwoOutline,
|
||||||
ExpandOutline,
|
ExpandOutline,
|
||||||
PowerOutline, LogoGithub, MoveOutline, WalletOutline, StarOutline, AlarmOutline, SparklesOutline,
|
PowerOutline, LogoGithub, MoveOutline, WalletOutline, StarOutline, AlarmOutline, SparklesOutline, NewspaperOutline,
|
||||||
} from '@vicons/ionicons5'
|
} from '@vicons/ionicons5'
|
||||||
import {GetConfig} from "../wailsjs/go/main/App";
|
import {GetConfig} from "../wailsjs/go/main/App";
|
||||||
const enableNews= ref(false)
|
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: () =>
|
label: () =>
|
||||||
h(
|
h(
|
||||||
@ -246,7 +262,7 @@ onMounted(()=>{
|
|||||||
:y-offset="150"
|
:y-offset="150"
|
||||||
:rotate="-15"
|
:rotate="-15"
|
||||||
>
|
>
|
||||||
<n-flex justify="center">
|
<n-flex>
|
||||||
<n-grid x-gap="12" :cols="1">
|
<n-grid x-gap="12" :cols="1">
|
||||||
<!--
|
<!--
|
||||||
<n-gi style="position: relative;top:1px;z-index: 19;width: 100%" v-if="telegraph.length>0">
|
<n-gi style="position: relative;top:1px;z-index: 19;width: 100%" v-if="telegraph.length>0">
|
||||||
|
71
frontend/src/components/market.vue
Normal file
71
frontend/src/components/market.vue
Normal 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>
|
@ -4,12 +4,14 @@ import stockView from '../components/stock.vue'
|
|||||||
import settingsView from '../components/settings.vue'
|
import settingsView from '../components/settings.vue'
|
||||||
import about from "../components/about.vue";
|
import about from "../components/about.vue";
|
||||||
import fundView from "../components/fund.vue";
|
import fundView from "../components/fund.vue";
|
||||||
|
import market from "../components/market.vue";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{ path: '/', component: stockView,name: 'stock' },
|
{ path: '/', component: stockView,name: 'stock' },
|
||||||
{ path: '/fund', component: fundView,name: 'fund' },
|
{ path: '/fund', component: fundView,name: 'fund' },
|
||||||
{ path: '/settings', component: settingsView,name: 'settings' },
|
{ path: '/settings', component: settingsView,name: 'settings' },
|
||||||
{ path: '/about', component: about,name: 'about' },
|
{ path: '/about', component: about,name: 'about' },
|
||||||
|
{ path: '/market', component: market,name: 'market' },
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
|
2
frontend/wailsjs/go/main/App.d.ts
vendored
2
frontend/wailsjs/go/main/App.d.ts
vendored
@ -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 GetStockList(arg1:string):Promise<Array<data.StockBasic>>;
|
||||||
|
|
||||||
|
export function GetTelegraphList():Promise<any>;
|
||||||
|
|
||||||
export function GetVersionInfo():Promise<models.VersionInfo>;
|
export function GetVersionInfo():Promise<models.VersionInfo>;
|
||||||
|
|
||||||
export function GetfundList(arg1:string):Promise<Array<data.FundBasic>>;
|
export function GetfundList(arg1:string):Promise<Array<data.FundBasic>>;
|
||||||
|
@ -74,6 +74,10 @@ export function GetStockList(arg1) {
|
|||||||
return window['go']['main']['App']['GetStockList'](arg1);
|
return window['go']['main']['App']['GetStockList'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GetTelegraphList() {
|
||||||
|
return window['go']['main']['App']['GetTelegraphList']();
|
||||||
|
}
|
||||||
|
|
||||||
export function GetVersionInfo() {
|
export function GetVersionInfo() {
|
||||||
return window['go']['main']['App']['GetVersionInfo']();
|
return window['go']['main']['App']['GetVersionInfo']();
|
||||||
}
|
}
|
||||||
|
31
main.go
31
main.go
@ -68,26 +68,29 @@ func main() {
|
|||||||
db.Dao.AutoMigrate(&models.PromptTemplate{})
|
db.Dao.AutoMigrate(&models.PromptTemplate{})
|
||||||
db.Dao.AutoMigrate(&data.Group{})
|
db.Dao.AutoMigrate(&data.Group{})
|
||||||
db.Dao.AutoMigrate(&data.GroupStock{})
|
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{
|
//db.Dao.Model(&data.Group{}).Where("id = ?", 0).FirstOrCreate(&data.Group{
|
||||||
// Name: "默认分组",
|
// Name: "默认分组",
|
||||||
// Sort: 0,
|
// Sort: 0,
|
||||||
//})
|
//})
|
||||||
|
|
||||||
if stocksBin != nil && len(stocksBin) > 0 {
|
//if stocksBin != nil && len(stocksBin) > 0 {
|
||||||
go initStockData()
|
// go initStockData()
|
||||||
}
|
//}
|
||||||
log.SugaredLogger.Infof("init stocksBinHK %d", len(stocksBinHK))
|
//log.SugaredLogger.Infof("init stocksBinHK %d", len(stocksBinHK))
|
||||||
|
//
|
||||||
if stocksBinHK != nil && len(stocksBinHK) > 0 {
|
//if stocksBinHK != nil && len(stocksBinHK) > 0 {
|
||||||
go initStockDataHK()
|
// go initStockDataHK()
|
||||||
}
|
//}
|
||||||
log.SugaredLogger.Infof("init stocksBinUS %d", len(stocksBinUS))
|
//log.SugaredLogger.Infof("init stocksBinUS %d", len(stocksBinUS))
|
||||||
|
//
|
||||||
if stocksBinUS != nil && len(stocksBinUS) > 0 {
|
//if stocksBinUS != nil && len(stocksBinUS) > 0 {
|
||||||
go initStockDataUS()
|
// go initStockDataUS()
|
||||||
}
|
//}
|
||||||
updateBasicInfo()
|
//updateBasicInfo()
|
||||||
|
|
||||||
// Create an instance of the app structure
|
// Create an instance of the app structure
|
||||||
app := NewApp()
|
app := NewApp()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user