feat(frontend):个股卡片中添加按钮,可以直接跳转到个股研报和公司公告页面,查询对应个股的研报或公告

- 在 market.vue 中添加个股研报和公司公告组件
- 在 stock.vue 中增加研报和公告的搜索功能
- 修改 StockNoticeList 和 StockResearchReportList 组件,支持接收 stockCode 参数
- 在 backend 中添加 TradingView 新闻 API 接口
This commit is contained in:
ArvinLovegood 2025-06-20 18:43:11 +08:00
parent f6d217e4fd
commit a936dc6371
8 changed files with 140 additions and 24 deletions

View File

@ -425,6 +425,15 @@ func (m MarketNewsApi) StockResearchReport(stockCode string, days int) []any {
if strutil.ContainsAny(stockCode, []string{"."}) {
stockCode = strings.Split(stockCode, ".")[0]
beginDate = time.Now().Add(-time.Duration(days) * 365 * time.Hour).Format("2006-01-02")
} else {
stockCode = strutil.ReplaceWithMap(stockCode, map[string]string{
"sh": "",
"sz": "",
"gb_": "",
"us": "",
"us_": "",
})
beginDate = time.Now().Add(-time.Duration(days) * 365 * time.Hour).Format("2006-01-02")
}
logger.SugaredLogger.Infof("StockResearchReport-stockCode:%s", stockCode)
@ -481,6 +490,15 @@ func (m MarketNewsApi) StockNotice(stock_list string) []any {
if strutil.ContainsAny(stockCode, []string{"."}) {
stockCode = strings.Split(stockCode, ".")[0]
stockCodes = append(stockCodes, stockCode)
} else {
stockCode = strutil.ReplaceWithMap(stockCode, map[string]string{
"sh": "",
"sz": "",
"gb_": "",
"us": "",
"us_": "",
})
stockCodes = append(stockCodes, stockCode)
}
}
@ -530,3 +548,29 @@ func (m MarketNewsApi) EMDictCode(code string, cache *freecache.Cache) []any {
cache.Set([]byte(code), resp.Body(), 60*60*24)
return respMap["data"].([]any)
}
func (m MarketNewsApi) TradingViewNews() *[]models.TVNews {
TVNews := &[]models.TVNews{}
url := "https://news-mediator.tradingview.com/news-flow/v2/news?filter=lang:zh-Hans&filter=provider:panews,reuters&client=screener&streaming=false"
resp, err := resty.New().SetProxy("http://127.0.0.1:10809").SetTimeout(time.Duration(30)*time.Second).R().
SetHeader("Host", "news-mediator.tradingview.com").
SetHeader("Origin", "https://cn.tradingview.com").
SetHeader("Referer", "https://cn.tradingview.com/").
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0").
Get(url)
if err != nil {
logger.SugaredLogger.Errorf("TradingViewNews err:%s", err.Error())
return TVNews
}
respMap := map[string]any{}
err = json.Unmarshal(resp.Body(), &respMap)
if err != nil {
return TVNews
}
items, err := json.Marshal(respMap["items"])
if err != nil {
return TVNews
}
json.Unmarshal(items, TVNews)
return TVNews
}

View File

@ -100,3 +100,11 @@ func TestEMDictCode(t *testing.T) {
}
}
func TestTradingViewNews(t *testing.T) {
db.Init("../../data/stock.db")
resp := NewMarketNewsApi().TradingViewNews()
for _, a := range *resp {
logger.SugaredLogger.Debugf("value: %+v", a)
}
}

View File

@ -2,6 +2,7 @@ package data
import (
"fmt"
"strings"
"testing"
)
@ -19,6 +20,10 @@ func TestAnalyzeSentiment(t *testing.T) {
//text = " 【三大指数均跌逾1% 下跌个股近4800只】财联社6月19日电指数持续走弱沪指下挫跌逾1.00%深成指跌1.25%创业板指跌1.39%。核聚变、风电、军工、食品消费等板块指数跌幅居前沪深京三市下跌个股近4800只。\n"
text = "【银行理财首单网下打新落地】财联社6月20日电记者从多渠道获悉光大理财以申报价格17元参与信通电子网下打新并成功入围有效报价成为行业内首家参与网下打新的银行理财公司。光大理财工作人员向证券时报记者表示本次光大理财是以其管理的混合类产品“阳光橙增盈绝对收益策略”参与了此次网下打新该产品为光大理财“固收+”银行理财产品。资料显示信通电子成立于1996年核心产品包括输电线路智能巡检系统、变电站智能辅控系统、移动智能终端及其他产品。根据其招股说明书信通电子2023、2024年营业收入分别较上年增长19.08%和7.97%净利润分别较上年增长5.6%和15.11%。 (证券时报)"
text = " 【以军称拦截数枚伊朗导弹】财联社6月20日电据央视新闻报道以军在贝尔谢巴及周边区域拦截了数枚伊朗导弹但仍有导弹或拦截残骸落地。以色列国防军发文表示搜救队伍正在一处“空中物体落地”的所在区域开展工作公众目前可以离开避难场所。伊朗方面对上述说法暂无回应。"
words := splitWords(text)
fmt.Println(strings.Join(words, " "))
result := AnalyzeSentiment(text)
// 输出结果

View File

@ -303,3 +303,17 @@ type LongTigerRankData struct {
func (l LongTigerRankData) TableName() string {
return "long_tiger_rank"
}
type TVNews struct {
Id string `json:"id"`
Title string `json:"title"`
Published int `json:"published"`
Urgency int `json:"urgency"`
Permission string `json:"permission"`
StoryPath string `json:"storyPath"`
Provider struct {
Id string `json:"id"`
Name string `json:"name"`
LogoId string `json:"logo_id"`
} `json:"provider"`
}

View File

@ -6,11 +6,20 @@ import {RefreshCircleSharp} from "@vicons/ionicons5";
import _ from "lodash";
import KLineChart from "./KLineChart.vue";
import MoneyTrend from "./moneyTrend.vue";
import {useMessage} from "naive-ui";
const {stockCode}=defineProps(
{
stockCode: {
type: String,
default: ''
}
}
)
const list = ref([])
const options = ref([])
const message=useMessage()
function getNotice(stockCodes) {
StockNotice(stockCodes).then(result => {
console.log(result)
@ -19,7 +28,8 @@ function getNotice(stockCodes) {
}
onBeforeMount (()=>{
getNotice('');
//message.info(""+stockCode)
getNotice(stockCode);
})
function findStockList(query){

View File

@ -8,6 +8,15 @@ import MoneyTrend from "./moneyTrend.vue";
import {useMessage} from "naive-ui";
import {BrowserOpenURL} from "../../wailsjs/runtime";
const {stockCode}=defineProps(
{
stockCode: {
type: String,
default: ''
}
}
)
const message=useMessage()
const list = ref([])
@ -21,7 +30,7 @@ function getStockResearchReport(value) {
}
onBeforeMount(()=>{
getStockResearchReport('');
getStockResearchReport(stockCode);
})
function ratingChangeName(ratingChange){

View File

@ -63,6 +63,7 @@ const sort = ref("0")
const nowTab = ref("市场快讯")
const indexInterval = ref(null)
const indexIndustryRank = ref(null)
const stockCode= ref('')
function getIndex() {
GlobalStockIndexes().then((res) => {
@ -79,6 +80,7 @@ function getIndex() {
onBeforeMount(() => {
nowTab.value = route.query.name
stockCode.value = route.query.stockCode
GetConfig().then(result => {
summaryBTN.value = result.openAiEnable
darkTheme.value = result.darkTheme
@ -547,10 +549,10 @@ function ReFlesh(source) {
<LongTigerRankList />
</n-tab-pane>
<n-tab-pane name="个股研报" tab="个股研报">
<StockResearchReportList/>
<StockResearchReportList :stock-code="stockCode"/>
</n-tab-pane>
<n-tab-pane name="公司公告" tab="公司公告 ">
<StockNoticeList/>
<StockNoticeList :stock-code="stockCode" />
</n-tab-pane>
<n-tab-pane name="行业研究" tab="行业研究 ">
<IndustryResearchReportList/>

View File

@ -1609,6 +1609,25 @@ function delStockGroup(code,name,groupId){
message.info(result)
})
}
function searchNotice(stockCode){
router.push({
name: 'market',
query: {
name: '公司公告',
stockCode: stockCode,
},
})
}
function searchStockReport(stockCode){
router.push({
name: 'market',
query: {
name: '个股研报',
stockCode: stockCode,
},
})
}
</script>
<template>
@ -1711,22 +1730,25 @@ function delStockGroup(code,name,groupId){
</template>
<template #footer>
<n-flex justify="center">
<n-text :type="'info'">{{result["日期"]+" "+result["时间"]}}</n-text>
<n-tag size="small" v-if="result.volume>0" :type="result.profitType">{{result.volume+""}}</n-tag>
<n-tag size="small" v-if="result.costPrice>0" :type="result.profitType">{{"成本:"+result.costPrice+"*"+result.costVolume+" "+result.profit+"%"+" ( "+result.profitAmount+" ¥ )"}}</n-tag>
</n-flex>
</template>
<template #action>
<n-flex justify="space-between">
<n-text :type="'info'">{{result["日期"]+" "+result["时间"]}}</n-text>
<n-button size="tiny" type="info" @click="setStock(result['股票代码'],result['股票名称'])"> 成本 </n-button>
<n-button size="tiny" type="success" @click="showFenshi(result['股票代码'],result['股票名称'],result.changePercent)"> 分时 </n-button>
<n-flex justify="left">
<n-button size="tiny" type="warning" @click="setStock(result['股票代码'],result['股票名称'])"> 成本 </n-button>
<n-button size="tiny" type="error" @click="showFenshi(result['股票代码'],result['股票名称'],result.changePercent)"> 分时 </n-button>
<n-button size="tiny" type="error" @click="showK(result['股票代码'],result['股票名称'])"> 日K </n-button>
<n-button size="tiny" type="error" v-if="result['买一报价']>0" @click="showMoney(result['股票代码'],result['股票名称'])"> 资金 </n-button>
<n-button size="tiny" type="warning" @click="search(result['股票代码'],result['股票名称'])"> 详情 </n-button>
<n-dropdown trigger="click" :options="groupList" key-field="ID" label-field="name" @select="(groupId) => AddStockGroupInfo(groupId,result['股票代码'],result['股票名称'])">
<n-button type="success" size="tiny">设置分组</n-button>
</n-dropdown>
<n-button size="tiny" type="success" @click="search(result['股票代码'],result['股票名称'])"> 详情 </n-button>
<n-button v-if="result['买一报价']>0" size="tiny" type="success" @click="searchNotice(result['股票代码'])"> 公告 </n-button>
<n-button v-if="result['买一报价']>0" size="tiny" type="success" @click="searchStockReport(result['股票代码'])"> 研报 </n-button>
<n-flex justify="right">
<n-dropdown trigger="click" :options="groupList" key-field="ID" label-field="name" @select="(groupId) => AddStockGroupInfo(groupId,result['股票代码'],result['股票名称'])">
<n-button type="warning" size="tiny">设置分组</n-button>
</n-dropdown>
</n-flex>
</n-flex>
</template>
</n-card >
@ -1824,23 +1846,25 @@ function delStockGroup(code,name,groupId){
</template>
<template #footer>
<n-flex justify="center">
<n-text :type="'info'">{{result["日期"]+" "+result["时间"]}}</n-text>
<n-tag size="small" v-if="result.volume>0" :type="result.profitType">{{result.volume+""}}</n-tag>
<n-tag size="small" v-if="result.costPrice>0" :type="result.profitType">{{"成本:"+result.costPrice+"*"+result.costVolume+" "+result.profit+"%"+" ( "+result.profitAmount+" ¥ )"}}</n-tag>
</n-flex>
</template>
<template #action>
<n-flex justify="space-between">
<n-text :type="'info'">{{result["日期"]+" "+result["时间"]}}</n-text>
<n-button size="tiny" type="info" @click="setStock(result['股票代码'],result['股票名称'])"> 成本 </n-button>
<n-button size="tiny" type="success" @click="showFenshi(result['股票代码'],result['股票名称'],result.changePercent)"> 分时 </n-button>
<n-flex justify="left">
<n-button size="tiny" type="warning" @click="setStock(result['股票代码'],result['股票名称'])"> 成本 </n-button>
<n-button size="tiny" type="error" @click="showFenshi(result['股票代码'],result['股票名称'],result.changePercent)"> 分时 </n-button>
<n-button size="tiny" type="error" @click="showK(result['股票代码'],result['股票名称'])"> 日K </n-button>
<n-button size="tiny" type="error" v-if="result['买一报价']>0" @click="showMoney(result['股票代码'],result['股票名称'])"> 资金 </n-button>
<n-button size="tiny" type="warning" @click="search(result['股票代码'],result['股票名称'])"> 详情 </n-button>
<n-dropdown trigger="click" :options="groupList" key-field="ID" label-field="name" @select="(groupId) => AddStockGroupInfo(groupId,result['股票代码'],result['股票名称'])">
<n-button type="success" size="tiny">设置分组</n-button>
</n-dropdown>
<n-button size="tiny" type="success" @click="search(result['股票代码'],result['股票名称'])"> 详情 </n-button>
<n-button v-if="result['买一报价']>0" size="tiny" type="success" @click="searchNotice(result['股票代码'])"> 公告 </n-button>
<n-button v-if="result['买一报价']>0" size="tiny" type="success" @click="searchStockReport(result['股票代码'])"> 研报 </n-button>
<n-flex justify="right">
<n-dropdown trigger="click" :options="groupList" key-field="ID" label-field="name" @select="(groupId) => AddStockGroupInfo(groupId,result['股票代码'],result['股票名称'])">
<n-button type="warning" size="tiny">设置分组</n-button>
</n-dropdown>
</n-flex>
</n-flex>
</template>
</n-card >