mirror of
https://github.com/ArvinLovegood/go-stock.git
synced 2025-07-19 00:00:09 +08:00
Compare commits
84 Commits
v2025.5.16
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
6116e9287a | ||
|
3e16574faa | ||
|
482472af4e | ||
|
bdc3689ac8 | ||
|
e8ebb577b2 | ||
|
71f8265bc2 | ||
|
43063fa7fb | ||
|
86f041b4d6 | ||
|
0ce7e8e7a7 | ||
|
bbab60e2ad | ||
|
1fbd564bff | ||
|
f0ad50303e | ||
|
55839d3329 | ||
|
3f4cbca4a7 | ||
|
6e3b9ff1f9 | ||
|
0e45866421 | ||
|
e0225c4158 | ||
|
2f6c17fb2a | ||
|
22b4fcdffb | ||
|
7dd10d443e | ||
|
5b5590ebd7 | ||
|
be02343d68 | ||
|
942d249671 | ||
|
9f2719cdbc | ||
|
0343a95a21 | ||
|
9337084ebf | ||
|
18834d9281 | ||
|
9e06136983 | ||
|
30a3d1d9ef | ||
|
5b6de9f9f6 | ||
|
65d737c695 | ||
|
af73691b22 | ||
|
b2c12cffbb | ||
|
a936dc6371 | ||
|
f6d217e4fd | ||
|
378b669827 | ||
|
0d3fd47552 | ||
|
a2fee361e7 | ||
|
1ef950b961 | ||
|
934b4608b7 | ||
|
68e7b6a68c | ||
|
700572567e | ||
|
c9ade36844 | ||
|
0a2491d725 | ||
|
9d8af191c5 | ||
|
6382be6b19 | ||
|
0cafcb9cd4 | ||
|
21c7f5390c | ||
|
02db6c2e87 | ||
|
2811786bfd | ||
|
9aa2c4095a | ||
|
ad9bea4c24 | ||
|
4f8d84b8a0 | ||
|
e238700333 | ||
|
6bdff0a0f3 | ||
|
c7655d2adf | ||
|
8996ddf986 | ||
|
329936568f | ||
|
0d85e24595 | ||
|
b266281bbd | ||
|
ace3ff7302 | ||
|
60b7cdc761 | ||
|
3cc597d361 | ||
|
68bcfc679a | ||
|
78f7808f1b | ||
|
d6c3a6b98b | ||
|
3b25aa79bb | ||
|
e49545a581 | ||
|
1185af5a87 | ||
|
152a6335d8 | ||
|
338e371190 | ||
|
3ffcaa0374 | ||
|
ed9d9cde77 | ||
|
673d446b05 | ||
|
e2e0ef2aad | ||
|
a8ecbf9329 | ||
|
9eded54d8d | ||
|
c1d458e5cf | ||
|
7158e405a6 | ||
|
d993a5525f | ||
|
6af6d989ba | ||
|
0b3acd9adc | ||
|
013de869f4 | ||
|
1b67e20932 |
4
.github/workflows/main.yml
vendored
4
.github/workflows/main.yml
vendored
@ -4,7 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
# Match any new tag
|
# Match any new tag
|
||||||
- '*'
|
- '*-release'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
# Necessary for most environments as build failure can occur due to OOM issues
|
# Necessary for most environments as build failure can occur due to OOM issues
|
||||||
@ -47,4 +47,4 @@ jobs:
|
|||||||
go-version: '1.24'
|
go-version: '1.24'
|
||||||
build-tags: ${{ github.ref_name }}
|
build-tags: ${{ github.ref_name }}
|
||||||
build-commit-message: ${{ steps.get_commit_message.outputs.commit_message }}
|
build-commit-message: ${{ steps.get_commit_message.outputs.commit_message }}
|
||||||
node-version: '18.x'
|
node-version: '20.x'
|
||||||
|
38
README.md
38
README.md
@ -3,14 +3,15 @@
|
|||||||

|

|
||||||
[](https://github.com/ArvinLovegood/go-stock)
|
[](https://github.com/ArvinLovegood/go-stock)
|
||||||
[](https://gitee.com/arvinlovegood_admin/go-stock)
|
[](https://gitee.com/arvinlovegood_admin/go-stock)
|
||||||
[](https://gitcode.com/ArvinLovegood/go-stock)
|
|
||||||
|
[//]: # ([](https://gitcode.com/ArvinLovegood/go-stock))
|
||||||
|
|
||||||
### 🌟公众号
|
### 🌟公众号
|
||||||

|

|
||||||
|
|
||||||
### 📈 交流群
|
### 📈 交流群
|
||||||
- QQ交流群2:[点击链接加入群聊【go-stock交流群2】:892666282](https://qm.qq.com/q/5mYiy6Yxh0)
|
- QQ交流群2:[点击链接加入群聊【go-stock交流群2】:892666282](https://qm.qq.com/q/5mYiy6Yxh0)
|
||||||
- QQ交流群:[点击链接加入群聊【go-stock交流群】:491605333(已满)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0YQ8qD3exahsD4YLNhzQTWe5ssstWC89&authKey=usOMMRFtIQDC%2FYcatHYapcxQbJ7PwXPHK9OypTXWzNjAq%2FRVvQu9bj2lRgb%2BSZ3p&noverify=0&group_code=491605333)
|
- QQ交流群:[点击链接加入群聊【go-stock交流群】:491605333(已满会定期清理,随缘入群)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0YQ8qD3exahsD4YLNhzQTWe5ssstWC89&authKey=usOMMRFtIQDC%2FYcatHYapcxQbJ7PwXPHK9OypTXWzNjAq%2FRVvQu9bj2lRgb%2BSZ3p&noverify=0&group_code=491605333)
|
||||||
|
|
||||||
### ✨ 简介
|
### ✨ 简介
|
||||||
- 本项目基于Wails和NaiveUI开发,结合AI大模型构建的股票分析工具。
|
- 本项目基于Wails和NaiveUI开发,结合AI大模型构建的股票分析工具。
|
||||||
@ -25,16 +26,17 @@
|
|||||||
|
|
||||||
|
|
||||||
### 💬 支持大模型/平台
|
### 💬 支持大模型/平台
|
||||||
| 模型 | 状态 | 备注 |
|
| 模型 | 状态 | 备注 |
|
||||||
| --- | --- |-----------------------------------------------------------------------------------------------------------------------------------------------------|
|
| --- | --- |---------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| [OpenAI](https://platform.openai.com/) | ✅ | 可接入任何 OpenAI 接口格式模型 |
|
| [OpenAI](https://platform.openai.com/) | ✅ | 可接入任何 OpenAI 接口格式模型 |
|
||||||
| [Ollama](https://ollama.com/) | ✅ | 本地大模型运行平台 |
|
| [Ollama](https://ollama.com/) | ✅ | 本地大模型运行平台 |
|
||||||
| [LMStudio](https://lmstudio.ai/) | ✅ | 本地大模型运行平台 |
|
| [LMStudio](https://lmstudio.ai/) | ✅ | 本地大模型运行平台 |
|
||||||
| [AnythingLLM](https://anythingllm.com/) | ✅ | 本地知识库 |
|
| [AnythingLLM](https://anythingllm.com/) | ✅ | 本地知识库 |
|
||||||
| [DeepSeek](https://www.deepseek.com/) | ✅ | deepseek-reasoner,deepseek-chat |
|
| [DeepSeek](https://www.deepseek.com/) | ✅ | deepseek-reasoner,deepseek-chat |
|
||||||
| [大模型聚合平台](https://cloud.siliconflow.cn/i/foufCerk) | ✅ | 如:[硅基流动](https://cloud.siliconflow.cn/i/foufCerk),[火山方舟](https://www.volcengine.com/experience/ark?utm_term=202502dsinvite&ac=DSASUQY5&rc=IJSE43PZ) |
|
| [大模型聚合平台](https://cloud.siliconflow.cn/i/foufCerk) | ✅ | 如:[硅基流动](https://cloud.siliconflow.cn/i/foufCerk),[火山方舟](https://www.volcengine.com/experience/ark?utm_term=202502dsinvite&ac=DSASUQY5&rc=IJSE43PZ) ,[优云智算](https://www.compshare.cn/image-community?ytag=GPU_YY-gh_gostock) |
|
||||||
|
|
||||||
### <span style="color: #568DF4;">各位亲爱的朋友们,如果您对这个项目感兴趣,请先给我一个<i style="color: #EA2626;">star</i>吧,谢谢!</span>💕
|
### <span style="color: #568DF4;">各位亲爱的朋友们,如果您对这个项目感兴趣,请先给我一个<i style="color: #EA2626;">star</i>吧,谢谢!</span>💕
|
||||||
|
- 优云智算(by UCloud):万卡规模4090免费用10小时,新人注册另增50万tokens,海量热门源项目镜像一键部署,[注册链接](https://www.compshare.cn/image-community?ytag=GPU_YY-gh_gostock)
|
||||||
- 经测试目前硅基流动(siliconflow)提供的deepSeek api 服务比较稳定,注册即送2000万Tokens,[注册链接](https://cloud.siliconflow.cn/i/foufCerk)
|
- 经测试目前硅基流动(siliconflow)提供的deepSeek api 服务比较稳定,注册即送2000万Tokens,[注册链接](https://cloud.siliconflow.cn/i/foufCerk)
|
||||||
- 火山方舟:每个模型注册即送50万tokens,[注册链接](https://www.volcengine.com/experience/ark?utm_term=202502dsinvite&ac=DSASUQY5&rc=IJSE43PZ)
|
- 火山方舟:每个模型注册即送50万tokens,[注册链接](https://www.volcengine.com/experience/ark?utm_term=202502dsinvite&ac=DSASUQY5&rc=IJSE43PZ)
|
||||||
- Tushare大数据开放社区,免费提供各类金融数据,助力行业和量化研究(注意:Tushare只需要120积分即可,注册完成个人资料补充即可得120积分!!!),[注册链接](https://tushare.pro/register?reg=701944)
|
- Tushare大数据开放社区,免费提供各类金融数据,助力行业和量化研究(注意:Tushare只需要120积分即可,注册完成个人资料补充即可得120积分!!!),[注册链接](https://tushare.pro/register?reg=701944)
|
||||||
@ -45,6 +47,8 @@
|
|||||||
## 🧩 重大功能开发计划
|
## 🧩 重大功能开发计划
|
||||||
| 功能说明 | 状态 | 备注 |
|
| 功能说明 | 状态 | 备注 |
|
||||||
|-----------------|----|----------------------------------------------------------------------------------------------------------|
|
|-----------------|----|----------------------------------------------------------------------------------------------------------|
|
||||||
|
| 股票分析知识库 | 🚧 | 未来计划 |
|
||||||
|
| Ai智能选股 | 🚧 | Ai智能选股功能开发中(下半年重点开发计划) |
|
||||||
| ETF支持 | 🚧 | ETF数据支持 (目前可以查看净值和估值) |
|
| ETF支持 | 🚧 | ETF数据支持 (目前可以查看净值和估值) |
|
||||||
| 美股支持 | ✅ | 美股数据支持 |
|
| 美股支持 | ✅ | 美股数据支持 |
|
||||||
| 港股支持 | ✅ | 港股数据支持 |
|
| 港股支持 | ✅ | 港股数据支持 |
|
||||||
@ -53,6 +57,20 @@
|
|||||||
| 不再强制依赖Chrome浏览器 | ✅ | 默认使用edge浏览器抓取新闻资讯 |
|
| 不再强制依赖Chrome浏览器 | ✅ | 默认使用edge浏览器抓取新闻资讯 |
|
||||||
|
|
||||||
## 👀 更新日志
|
## 👀 更新日志
|
||||||
|
### 2025.06.30 添加指标选股功能
|
||||||
|
### 2025.06.27 添加财经日历和重大事件时间轴功能
|
||||||
|
### 2025.06.25 添加热门股票、事件和话题功能
|
||||||
|
### 2025.06.18 更新内置股票基础数据,软件内实时市场资讯信息提醒,添加行业研究功能
|
||||||
|
### 2025.06.15 添加公司公告信息搜索/查看功能
|
||||||
|
### 2025.06.15 添加个股研报到弹出菜单
|
||||||
|
### 2025.06.13 添加个股研报功能
|
||||||
|
### 2025.06.12 添加龙虎榜功能,新增行业排名分类
|
||||||
|
### 2025.05.30 优化股票分时图显示
|
||||||
|
### 2025.05.20 修复财联社电报获取问题
|
||||||
|
### 2025.05.16 优化资金趋势图表组件
|
||||||
|
### 2025.05.15 重构应用加载和数据初始化逻辑,添加股票资金趋势功能,资金趋势图表增加主力当日净流入数据并优化展示效果
|
||||||
|
### 2025.05.14 添加个股资金流向功能,排行榜增加股票行情K线图弹窗
|
||||||
|
### 2025.05.13 添加行业排名功能
|
||||||
### 2025.05.09 添加A股盘口数据解析和展示功能
|
### 2025.05.09 添加A股盘口数据解析和展示功能
|
||||||
### 2025.05.07 优化分时图的展示
|
### 2025.05.07 优化分时图的展示
|
||||||
### 2025.04.29 补全港股/美股基础数据,优化港股股价延迟问题,优化初始化逻辑
|
### 2025.04.29 补全港股/美股基础数据,优化港股股价延迟问题,优化初始化逻辑
|
||||||
|
19
app.go
19
app.go
@ -178,6 +178,9 @@ func (a *App) domReady(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
entryID, err := a.cron.AddFunc(fmt.Sprintf("@every %ds", interval+10), func() {
|
entryID, err := a.cron.AddFunc(fmt.Sprintf("@every %ds", interval+10), func() {
|
||||||
news := data.NewMarketNewsApi().GetNewTelegraph(30)
|
news := data.NewMarketNewsApi().GetNewTelegraph(30)
|
||||||
|
if config.EnablePushNews {
|
||||||
|
go a.NewsPush(news)
|
||||||
|
}
|
||||||
go runtime.EventsEmit(a.ctx, "newTelegraph", news)
|
go runtime.EventsEmit(a.ctx, "newTelegraph", news)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -188,6 +191,9 @@ func (a *App) domReady(ctx context.Context) {
|
|||||||
|
|
||||||
entryIDSina, err := a.cron.AddFunc(fmt.Sprintf("@every %ds", interval+10), func() {
|
entryIDSina, err := a.cron.AddFunc(fmt.Sprintf("@every %ds", interval+10), func() {
|
||||||
news := data.NewMarketNewsApi().GetSinaNews(30)
|
news := data.NewMarketNewsApi().GetSinaNews(30)
|
||||||
|
if config.EnablePushNews {
|
||||||
|
go a.NewsPush(news)
|
||||||
|
}
|
||||||
go runtime.EventsEmit(a.ctx, "newSinaNews", news)
|
go runtime.EventsEmit(a.ctx, "newSinaNews", news)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -292,6 +298,15 @@ func (a *App) domReady(ctx context.Context) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *App) NewsPush(news *[]models.Telegraph) {
|
||||||
|
for _, telegraph := range *news {
|
||||||
|
//if telegraph.IsRed {
|
||||||
|
go runtime.EventsEmit(a.ctx, "newsPush", telegraph)
|
||||||
|
go data.NewAlertWindowsApi("go-stock", telegraph.Source+" "+telegraph.Time, telegraph.Content, string(icon)).SendNotification()
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) AddCronTask(follow data.FollowedStock) func() {
|
func (a *App) AddCronTask(follow data.FollowedStock) func() {
|
||||||
return func() {
|
return func() {
|
||||||
go runtime.EventsEmit(a.ctx, "warnMsg", "开始自动分析"+follow.Name+"_"+follow.StockCode)
|
go runtime.EventsEmit(a.ctx, "warnMsg", "开始自动分析"+follow.Name+"_"+follow.StockCode)
|
||||||
@ -1123,8 +1138,8 @@ func (a *App) GetIndustryRank(sort string, cnt int) []any {
|
|||||||
res := data.NewMarketNewsApi().GetIndustryRank(sort, cnt)
|
res := data.NewMarketNewsApi().GetIndustryRank(sort, cnt)
|
||||||
return res["data"].([]any)
|
return res["data"].([]any)
|
||||||
}
|
}
|
||||||
func (a *App) GetIndustryMoneyRankSina(fenlei string) []map[string]any {
|
func (a *App) GetIndustryMoneyRankSina(fenlei, sort string) []map[string]any {
|
||||||
res := data.NewMarketNewsApi().GetIndustryMoneyRankSina(fenlei)
|
res := data.NewMarketNewsApi().GetIndustryMoneyRankSina(fenlei, sort)
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
func (a *App) GetMoneyRankSina(sort string) []map[string]any {
|
func (a *App) GetMoneyRankSina(sort string) []map[string]any {
|
||||||
|
61
app_common.go
Normal file
61
app_common.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-stock/backend/data"
|
||||||
|
"go-stock/backend/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Author spark
|
||||||
|
// @Date 2025/6/8 20:45
|
||||||
|
// @Desc
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func (a *App) LongTigerRank(date string) *[]models.LongTigerRankData {
|
||||||
|
return data.NewMarketNewsApi().LongTiger(date)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) StockResearchReport(stockCode string) []any {
|
||||||
|
return data.NewMarketNewsApi().StockResearchReport(stockCode, 7)
|
||||||
|
}
|
||||||
|
func (a *App) StockNotice(stockCode string) []any {
|
||||||
|
return data.NewMarketNewsApi().StockNotice(stockCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) IndustryResearchReport(industryCode string) []any {
|
||||||
|
return data.NewMarketNewsApi().IndustryResearchReport(industryCode, 7)
|
||||||
|
}
|
||||||
|
func (a App) EMDictCode(code string) []any {
|
||||||
|
return data.NewMarketNewsApi().EMDictCode(code, a.cache)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a App) AnalyzeSentiment(text string) data.SentimentResult {
|
||||||
|
return data.AnalyzeSentiment(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a App) HotStock(marketType string) *[]models.HotItem {
|
||||||
|
return data.NewMarketNewsApi().XUEQIUHotStock(100, 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a App) InvestCalendarTimeLine(yearMonth string) []any {
|
||||||
|
return data.NewMarketNewsApi().InvestCalendar(yearMonth)
|
||||||
|
}
|
||||||
|
func (a App) ClsCalendar() []any {
|
||||||
|
return data.NewMarketNewsApi().ClsCalendar()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a App) SearchStock(words string) map[string]any {
|
||||||
|
return data.NewSearchStockApi(words).SearchStock()
|
||||||
|
}
|
@ -1,13 +1,17 @@
|
|||||||
package data
|
package data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/PuerkitoBio/goquery"
|
"github.com/PuerkitoBio/goquery"
|
||||||
|
"github.com/coocood/freecache"
|
||||||
|
"github.com/duke-git/lancet/v2/convertor"
|
||||||
"github.com/duke-git/lancet/v2/strutil"
|
"github.com/duke-git/lancet/v2/strutil"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/robertkrimen/otto"
|
"github.com/robertkrimen/otto"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
|
"github.com/tidwall/gjson"
|
||||||
"go-stock/backend/db"
|
"go-stock/backend/db"
|
||||||
"go-stock/backend/logger"
|
"go-stock/backend/logger"
|
||||||
"go-stock/backend/models"
|
"go-stock/backend/models"
|
||||||
@ -37,7 +41,7 @@ func (m MarketNewsApi) GetNewTelegraph(crawlTimeOut int64) *[]models.Telegraph {
|
|||||||
//logger.SugaredLogger.Info(string(response.Body()))
|
//logger.SugaredLogger.Info(string(response.Body()))
|
||||||
document, _ := goquery.NewDocumentFromReader(strings.NewReader(string(response.Body())))
|
document, _ := goquery.NewDocumentFromReader(strings.NewReader(string(response.Body())))
|
||||||
|
|
||||||
document.Find(".telegraph-list").Each(func(i int, selection *goquery.Selection) {
|
document.Find(".telegraph-content-box").Each(func(i int, selection *goquery.Selection) {
|
||||||
//logger.SugaredLogger.Info(selection.Text())
|
//logger.SugaredLogger.Info(selection.Text())
|
||||||
telegraph := models.Telegraph{Source: "财联社电报"}
|
telegraph := models.Telegraph{Source: "财联社电报"}
|
||||||
spans := selection.Find("div.telegraph-content-box span")
|
spans := selection.Find("div.telegraph-content-box span")
|
||||||
@ -69,6 +73,7 @@ func (m MarketNewsApi) GetNewTelegraph(crawlTimeOut int64) *[]models.Telegraph {
|
|||||||
|
|
||||||
//telegraph = append(telegraph, ReplaceSensitiveWords(selection.Text()))
|
//telegraph = append(telegraph, ReplaceSensitiveWords(selection.Text()))
|
||||||
if telegraph.Content != "" {
|
if telegraph.Content != "" {
|
||||||
|
telegraph.SentimentResult = AnalyzeSentiment(telegraph.Content).Description
|
||||||
cnt := int64(0)
|
cnt := int64(0)
|
||||||
db.Dao.Model(telegraph).Where("time=? and source=?", telegraph.Time, telegraph.Source).Count(&cnt)
|
db.Dao.Model(telegraph).Where("time=? and source=?", telegraph.Time, telegraph.Source).Count(&cnt)
|
||||||
if cnt == 0 {
|
if cnt == 0 {
|
||||||
@ -138,11 +143,10 @@ func (m MarketNewsApi) GetSinaNews(crawlTimeOut uint) *[]models.Telegraph {
|
|||||||
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").
|
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("https://zhibo.sina.com.cn/api/zhibo/feed?callback=callback&page=1&page_size=20&zhibo_id=152&tag_id=0&dire=f&dpc=1&pagesize=20&id=4161089&type=0&_=" + strconv.FormatInt(time.Now().Unix(), 10))
|
Get("https://zhibo.sina.com.cn/api/zhibo/feed?callback=callback&page=1&page_size=20&zhibo_id=152&tag_id=0&dire=f&dpc=1&pagesize=20&id=4161089&type=0&_=" + strconv.FormatInt(time.Now().Unix(), 10))
|
||||||
js := string(response.Body())
|
js := string(response.Body())
|
||||||
js = strutil.ReplaceWithMap(js,
|
js = strutil.ReplaceWithMap(js, map[string]string{
|
||||||
map[string]string{
|
"try{callback(": "var data=",
|
||||||
"try{callback(": "var data=",
|
");}catch(e){};": ";",
|
||||||
");}catch(e){};": ";",
|
})
|
||||||
})
|
|
||||||
//logger.SugaredLogger.Info(js)
|
//logger.SugaredLogger.Info(js)
|
||||||
vm := otto.New()
|
vm := otto.New()
|
||||||
_, err := vm.Run(js)
|
_, err := vm.Run(js)
|
||||||
@ -189,6 +193,7 @@ func (m MarketNewsApi) GetSinaNews(crawlTimeOut uint) *[]models.Telegraph {
|
|||||||
logger.SugaredLogger.Infof("telegraph.SubjectTags:%v %s", telegraph.SubjectTags, telegraph.Content)
|
logger.SugaredLogger.Infof("telegraph.SubjectTags:%v %s", telegraph.SubjectTags, telegraph.Content)
|
||||||
|
|
||||||
if telegraph.Content != "" {
|
if telegraph.Content != "" {
|
||||||
|
telegraph.SentimentResult = AnalyzeSentiment(telegraph.Content).Description
|
||||||
cnt := int64(0)
|
cnt := int64(0)
|
||||||
db.Dao.Model(telegraph).Where("time=? and source=?", telegraph.Time, telegraph.Source).Count(&cnt)
|
db.Dao.Model(telegraph).Where("time=? and source=?", telegraph.Time, telegraph.Source).Count(&cnt)
|
||||||
if cnt == 0 {
|
if cnt == 0 {
|
||||||
@ -238,8 +243,8 @@ func (m MarketNewsApi) GetIndustryRank(sort string, cnt int) map[string]any {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m MarketNewsApi) GetIndustryMoneyRankSina(fenlei string) []map[string]any {
|
func (m MarketNewsApi) GetIndustryMoneyRankSina(fenlei, sort string) []map[string]any {
|
||||||
url := fmt.Sprintf("https://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/MoneyFlow.ssl_bkzj_bk?page=1&num=20&sort=netamount&asc=0&fenlei=%s", fenlei)
|
url := fmt.Sprintf("https://vip.stock.finance.sina.com.cn/quotes_service/api/json_v2.php/MoneyFlow.ssl_bkzj_bk?page=1&num=20&sort=%s&asc=0&fenlei=%s", sort, fenlei)
|
||||||
|
|
||||||
response, _ := resty.New().SetTimeout(time.Duration(5)*time.Second).R().
|
response, _ := resty.New().SetTimeout(time.Duration(5)*time.Second).R().
|
||||||
SetHeader("Host", "vip.stock.finance.sina.com.cn").
|
SetHeader("Host", "vip.stock.finance.sina.com.cn").
|
||||||
@ -293,3 +298,406 @@ func (m MarketNewsApi) GetStockMoneyTrendByDay(stockCode string, days int) []map
|
|||||||
return *res
|
return *res
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m MarketNewsApi) TopStocksRankingList(date string) {
|
||||||
|
url := fmt.Sprintf("http://vip.stock.finance.sina.com.cn/q/go.php/vInvestConsult/kind/lhb/index.phtml?tradedate=%s", date)
|
||||||
|
response, _ := resty.New().SetTimeout(time.Duration(5)*time.Second).R().
|
||||||
|
SetHeader("Host", "vip.stock.finance.sina.com.cn").
|
||||||
|
SetHeader("Referer", "https://finance.sina.com.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(url)
|
||||||
|
|
||||||
|
html, _ := convertor.GbkToUtf8(response.Body())
|
||||||
|
//logger.SugaredLogger.Infof("html:%s", html)
|
||||||
|
document, err := goquery.NewDocumentFromReader(bytes.NewReader(html))
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
document.Find("table.list_table").Each(func(i int, s *goquery.Selection) {
|
||||||
|
title := strutil.Trim(s.Find("tr:first-child").First().Text())
|
||||||
|
logger.SugaredLogger.Infof("title:%s", title)
|
||||||
|
s.Find("tr:not(:first-child)").Each(func(i int, s *goquery.Selection) {
|
||||||
|
logger.SugaredLogger.Infof("s:%s", strutil.RemoveNonPrintable(s.Text()))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MarketNewsApi) LongTiger(date string) *[]models.LongTigerRankData {
|
||||||
|
ranks := &[]models.LongTigerRankData{}
|
||||||
|
url := "https://datacenter-web.eastmoney.com/api/data/v1/get"
|
||||||
|
logger.SugaredLogger.Infof("url:%s", url)
|
||||||
|
params := make(map[string]string)
|
||||||
|
params["callback"] = "callback"
|
||||||
|
params["sortColumns"] = "TURNOVERRATE,TRADE_DATE,SECURITY_CODE"
|
||||||
|
params["sortTypes"] = "-1,-1,1"
|
||||||
|
params["pageSize"] = "500"
|
||||||
|
params["pageNumber"] = "1"
|
||||||
|
params["reportName"] = "RPT_DAILYBILLBOARD_DETAILSNEW"
|
||||||
|
params["columns"] = "SECURITY_CODE,SECUCODE,SECURITY_NAME_ABBR,TRADE_DATE,EXPLAIN,CLOSE_PRICE,CHANGE_RATE,BILLBOARD_NET_AMT,BILLBOARD_BUY_AMT,BILLBOARD_SELL_AMT,BILLBOARD_DEAL_AMT,ACCUM_AMOUNT,DEAL_NET_RATIO,DEAL_AMOUNT_RATIO,TURNOVERRATE,FREE_MARKET_CAP,EXPLANATION,D1_CLOSE_ADJCHRATE,D2_CLOSE_ADJCHRATE,D5_CLOSE_ADJCHRATE,D10_CLOSE_ADJCHRATE,SECURITY_TYPE_CODE"
|
||||||
|
params["source"] = "WEB"
|
||||||
|
params["client"] = "WEB"
|
||||||
|
params["filter"] = fmt.Sprintf("(TRADE_DATE<='%s')(TRADE_DATE>='%s')", date, date)
|
||||||
|
resp, err := resty.New().SetTimeout(time.Duration(15)*time.Second).R().
|
||||||
|
SetHeader("Host", "datacenter-web.eastmoney.com").
|
||||||
|
SetHeader("Referer", "https://data.eastmoney.com/stock/tradedetail.html").
|
||||||
|
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0").
|
||||||
|
SetQueryParams(params).
|
||||||
|
Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return ranks
|
||||||
|
}
|
||||||
|
js := string(resp.Body())
|
||||||
|
logger.SugaredLogger.Infof("resp:%s", js)
|
||||||
|
|
||||||
|
js = strutil.ReplaceWithMap(js, map[string]string{
|
||||||
|
"callback(": "var data=",
|
||||||
|
");": ";",
|
||||||
|
})
|
||||||
|
//logger.SugaredLogger.Info(js)
|
||||||
|
vm := otto.New()
|
||||||
|
_, err = vm.Run(js)
|
||||||
|
_, err = vm.Run("var data = JSON.stringify(data);")
|
||||||
|
value, err := vm.Get("data")
|
||||||
|
logger.SugaredLogger.Infof("resp-json:%s", value.String())
|
||||||
|
data := gjson.Get(value.String(), "result.data")
|
||||||
|
logger.SugaredLogger.Infof("resp:%v", data)
|
||||||
|
err = json.Unmarshal([]byte(data.String()), ranks)
|
||||||
|
if err != nil {
|
||||||
|
logger.SugaredLogger.Error(err)
|
||||||
|
return ranks
|
||||||
|
}
|
||||||
|
for _, rankData := range *ranks {
|
||||||
|
temp := &models.LongTigerRankData{}
|
||||||
|
db.Dao.Model(temp).Where(&models.LongTigerRankData{
|
||||||
|
TRADEDATE: rankData.TRADEDATE,
|
||||||
|
SECUCODE: rankData.SECUCODE,
|
||||||
|
}).First(temp)
|
||||||
|
if temp.SECURITYTYPECODE == "" {
|
||||||
|
db.Dao.Model(temp).Create(&rankData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ranks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MarketNewsApi) IndustryResearchReport(industryCode string, days int) []any {
|
||||||
|
beginDate := time.Now().Add(-time.Duration(days) * 24 * time.Hour).Format("2006-01-02")
|
||||||
|
endDate := time.Now().Format("2006-01-02")
|
||||||
|
if strutil.Trim(industryCode) != "" {
|
||||||
|
beginDate = time.Now().Add(-time.Duration(days) * 365 * time.Hour).Format("2006-01-02")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.SugaredLogger.Infof("IndustryResearchReport-name:%s", industryCode)
|
||||||
|
params := map[string]string{
|
||||||
|
"industry": "*",
|
||||||
|
"industryCode": industryCode,
|
||||||
|
"beginTime": beginDate,
|
||||||
|
"endTime": endDate,
|
||||||
|
"pageNo": "1",
|
||||||
|
"pageSize": "50",
|
||||||
|
"p": "1",
|
||||||
|
"pageNum": "1",
|
||||||
|
"pageNumber": "1",
|
||||||
|
"qType": "1",
|
||||||
|
}
|
||||||
|
|
||||||
|
url := "https://reportapi.eastmoney.com/report/list"
|
||||||
|
|
||||||
|
logger.SugaredLogger.Infof("beginDate:%s endDate:%s", beginDate, endDate)
|
||||||
|
resp, err := resty.New().SetTimeout(time.Duration(15)*time.Second).R().
|
||||||
|
SetHeader("Host", "reportapi.eastmoney.com").
|
||||||
|
SetHeader("Origin", "https://data.eastmoney.com").
|
||||||
|
SetHeader("Referer", "https://data.eastmoney.com/report/stock.jshtml").
|
||||||
|
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0").
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetQueryParams(params).Get(url)
|
||||||
|
respMap := map[string]any{}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return []any{}
|
||||||
|
}
|
||||||
|
json.Unmarshal(resp.Body(), &respMap)
|
||||||
|
//logger.SugaredLogger.Infof("resp:%+v", respMap["data"])
|
||||||
|
return respMap["data"].([]any)
|
||||||
|
}
|
||||||
|
func (m MarketNewsApi) StockResearchReport(stockCode string, days int) []any {
|
||||||
|
beginDate := time.Now().Add(-time.Duration(days) * 24 * time.Hour).Format("2006-01-02")
|
||||||
|
endDate := time.Now().Format("2006-01-02")
|
||||||
|
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)
|
||||||
|
|
||||||
|
type Req struct {
|
||||||
|
BeginTime string `json:"beginTime"`
|
||||||
|
EndTime string `json:"endTime"`
|
||||||
|
IndustryCode string `json:"industryCode"`
|
||||||
|
RatingChange string `json:"ratingChange"`
|
||||||
|
Rating string `json:"rating"`
|
||||||
|
OrgCode interface{} `json:"orgCode"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Rcode string `json:"rcode"`
|
||||||
|
PageSize int `json:"pageSize"`
|
||||||
|
PageNo int `json:"pageNo"`
|
||||||
|
P int `json:"p"`
|
||||||
|
PageNum int `json:"pageNum"`
|
||||||
|
PageNumber int `json:"pageNumber"`
|
||||||
|
}
|
||||||
|
|
||||||
|
url := "https://reportapi.eastmoney.com/report/list2"
|
||||||
|
|
||||||
|
logger.SugaredLogger.Infof("beginDate:%s endDate:%s", beginDate, endDate)
|
||||||
|
resp, err := resty.New().SetTimeout(time.Duration(15)*time.Second).R().
|
||||||
|
SetHeader("Host", "reportapi.eastmoney.com").
|
||||||
|
SetHeader("Origin", "https://data.eastmoney.com").
|
||||||
|
SetHeader("Referer", "https://data.eastmoney.com/report/stock.jshtml").
|
||||||
|
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0").
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetBody(&Req{
|
||||||
|
Code: stockCode,
|
||||||
|
IndustryCode: "*",
|
||||||
|
BeginTime: beginDate,
|
||||||
|
EndTime: endDate,
|
||||||
|
PageNo: 1,
|
||||||
|
PageSize: 50,
|
||||||
|
P: 1,
|
||||||
|
PageNum: 1,
|
||||||
|
PageNumber: 1,
|
||||||
|
}).Post(url)
|
||||||
|
respMap := map[string]any{}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return []any{}
|
||||||
|
}
|
||||||
|
json.Unmarshal(resp.Body(), &respMap)
|
||||||
|
//logger.SugaredLogger.Infof("resp:%+v", respMap["data"])
|
||||||
|
return respMap["data"].([]any)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MarketNewsApi) StockNotice(stock_list string) []any {
|
||||||
|
var stockCodes []string
|
||||||
|
for _, stockCode := range strings.Split(stock_list, ",") {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
url := "https://np-anotice-stock.eastmoney.com/api/security/ann?page_size=50&page_index=1&ann_type=SHA%2CCYB%2CSZA%2CBJA%2CINV&client_source=web&f_node=0&stock_list=" + strings.Join(stockCodes, ",")
|
||||||
|
resp, err := resty.New().SetTimeout(time.Duration(15)*time.Second).R().
|
||||||
|
SetHeader("Host", "np-anotice-stock.eastmoney.com").
|
||||||
|
SetHeader("Referer", "https://data.eastmoney.com/notices/hsa/5.html").
|
||||||
|
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0").
|
||||||
|
Get(url)
|
||||||
|
respMap := map[string]any{}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return []any{}
|
||||||
|
}
|
||||||
|
json.Unmarshal(resp.Body(), &respMap)
|
||||||
|
//logger.SugaredLogger.Infof("resp:%+v", respMap["data"])
|
||||||
|
return (respMap["data"].(map[string]any))["list"].([]any)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MarketNewsApi) EMDictCode(code string, cache *freecache.Cache) []any {
|
||||||
|
respMap := map[string]any{}
|
||||||
|
|
||||||
|
d, _ := cache.Get([]byte(code))
|
||||||
|
if d != nil {
|
||||||
|
json.Unmarshal(d, &respMap)
|
||||||
|
return respMap["data"].([]any)
|
||||||
|
}
|
||||||
|
|
||||||
|
url := "https://reportapi.eastmoney.com/report/bk"
|
||||||
|
|
||||||
|
params := map[string]string{
|
||||||
|
"bkCode": code,
|
||||||
|
}
|
||||||
|
resp, err := resty.New().SetTimeout(time.Duration(15)*time.Second).R().
|
||||||
|
SetHeader("Host", "reportapi.eastmoney.com").
|
||||||
|
SetHeader("Origin", "https://data.eastmoney.com").
|
||||||
|
SetHeader("Referer", "https://data.eastmoney.com/report/industry.jshtml").
|
||||||
|
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0").
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetQueryParams(params).Get(url)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return []any{}
|
||||||
|
}
|
||||||
|
json.Unmarshal(resp.Body(), &respMap)
|
||||||
|
//logger.SugaredLogger.Infof("resp:%+v", respMap["data"])
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MarketNewsApi) XUEQIUHotStock(size int, marketType string) *[]models.HotItem {
|
||||||
|
request := resty.New().SetTimeout(time.Duration(30) * time.Second).R()
|
||||||
|
_, err := request.
|
||||||
|
SetHeader("Host", "xueqiu.com").
|
||||||
|
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0").
|
||||||
|
Get("https://xueqiu.com/hq#hot")
|
||||||
|
|
||||||
|
//cookies := resp.Header().Get("Set-Cookie")
|
||||||
|
//logger.SugaredLogger.Infof("cookies:%s", cookies)
|
||||||
|
|
||||||
|
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 = request.
|
||||||
|
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=871730774144180; device_id=ee75cebba8a35005c9e7baf7b7dead59; s=ch12b12pfi; Hm_lvt_1db88642e346389874251b5a1eded6e3=1746247619; xq_a_token=361dcfccb1d32a1d9b5b65f1a188b9c9ed1e687d; xqat=361dcfccb1d32a1d9b5b65f1a188b9c9ed1e687d; xq_r_token=450d1db0db9659a6af7cc9297bfa4fccf1776fae; xq_id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1aWQiOi0xLCJpc3MiOiJ1YyIsImV4cCI6MTc1MzgzODAwNiwiY3RtIjoxNzUxMjUxMzc2MDY3LCJjaWQiOiJkOWQwbjRBWnVwIn0.TjEtQ5WEN4ajnVjVnY3J-Qq9LjL-F0eat9Cefv_tLJLqsPhzD2y8Lc1CeIu0Ceqhlad7O_yW1tR9nb2dIjDpyOPzWKxvwSOKXLm8XMoz4LMgE2pysBCH4TsetzHsEOhBsY467q-JX3WoFuqo-dqv1FfLSondZCspjEMFdgPFt2V-2iXJY05YUwcBVUvL74mT9ZjNq0KaDeRBJk_il6UR8yibG7RMbe9xWYz5dSO_wJwWuxvnZ8u9EXC2m-TV7-QHVxFHR_5e8Fodrzg0yIcLU4wBTSoIIQDUKqngajX2W-nUAdo6fr78NNDmoswFVH7T7XMuQciMAqj9MpMCVW3Sog; u=871730774144180; ssxmod_itna=iq+h7KAImDORKYQ4Y5G=nxBKDtD7D3qCD0dGMDxeq7tDRDFqApKDHtA68oon7ziBA0+PbZ9xGN4oYxiNDAPq0iDC+Wjxs9Orw5KQb9iqP4MAn0TbNsbtU22eqbCe=S3vTv6xoDHxY=DU1GzeieDx=PD5xDTDWeDGDD3DmnsDi5YD0KDjBYpH+omDYPDEBYDaxDbDimwY4GCrDDCtc5Dw6bmzDDzznL5WWAPzWffZg3YcFgxf8GwD7y3Dla4rMhw23=cz0Efdk0A5hYDXotDvhoY1/H6neEvOt3o=Q0ruT+5RuxoRhDxCmh5tGP32xBD5G0xS2xcb4quDK0Dy2ZmY/DDWM0qmEeSEDeOCIq1fw1misCY=WAzoOtMwDzGdUjpRk5Z0xQBDI2IMw4H7qNiNBLxWiDD; ssxmod_itna2=iq+h7KAImDORKYQ4Y5G=nxBKDtD7D3qCD0dGMDxeq7tDRDFqApKDHtA68oon7ziBA0+PbZYxD3boBmiEPtDFOEPAeFmDDsuGSxf46oGKwGHd8wtUjFe+oV1lxUzutkGly=nCyCjq=UTHxMxFCr1DsFiKPuEpPVO7GrOyk5Aymnc0+11AFND7v16PvwrFQH4I72=3O1OpK7rGw+poWNCxjj=Ka5QDFWAvEzrDFQcIH=GpKpS90FAyIzGcTyck+yhQKaojn96dRqeIh=HkaFrlGnKwzO+a49=F7/c/MejoR3QM20K9IIOymrMN2bsk2TRdKFiaf4O0ut2MauiOER=iQNW2WVgDrkKzD=57r577wEx2hwkqhf8T8BDvkHZRDirC0bNK4O=G3TSkd3wYwq8bst0t9qF/e3M87NYtU2IWYWzqd=BqEfdqGq0R8wxmqLzpeGeuwSTq1OAiB87gDrozjnGkwDKRdrLz8uDjQKVlGhWk8Wd/rXQjx4pG=BNqpW/6TS1wpfxzGf5CrUhtt0j0wC5AUFo2GbX+QXPzD2guxKXrx8lZUQlwWIHyEUz+OLh0eWUkfHfM0YWXlgOejnuUa06rW9y5maDPipGms751hxKcqLq62pQty4iX3QDF6SRQd3tfEBf3CH7r2xe2qq0qdOI5Ge=GezD/Us5Z0xQBwVAZ2N/XvD0HDD").
|
||||||
|
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)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MarketNewsApi) InvestCalendar(yearMonth string) []any {
|
||||||
|
if yearMonth == "" {
|
||||||
|
yearMonth = time.Now().Format("2006-01")
|
||||||
|
}
|
||||||
|
|
||||||
|
url := "https://app.jiuyangongshe.com/jystock-app/api/v1/timeline/list"
|
||||||
|
resp, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R().
|
||||||
|
SetHeader("Host", "app.jiuyangongshe.com").
|
||||||
|
SetHeader("Origin", "https://www.jiuyangongshe.com").
|
||||||
|
SetHeader("Referer", "https://www.jiuyangongshe.com/").
|
||||||
|
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0").
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetHeader("token", "1cc6380a05c652b922b3d85124c85473").
|
||||||
|
SetHeader("platform", "3").
|
||||||
|
SetHeader("Cookie", "SESSION=NDZkNDU2ODYtODEwYi00ZGZkLWEyY2ItNjgxYzY4ZWMzZDEy").
|
||||||
|
SetHeader("timestamp", strconv.FormatInt(time.Now().UnixMilli(), 10)).
|
||||||
|
SetBody(map[string]string{
|
||||||
|
"date": yearMonth,
|
||||||
|
"grade": "0",
|
||||||
|
}).
|
||||||
|
Post(url)
|
||||||
|
if err != nil {
|
||||||
|
logger.SugaredLogger.Errorf("InvestCalendar err:%s", err.Error())
|
||||||
|
return []any{}
|
||||||
|
}
|
||||||
|
//logger.SugaredLogger.Infof("InvestCalendar:%s", resp.Body())
|
||||||
|
respMap := map[string]any{}
|
||||||
|
err = json.Unmarshal(resp.Body(), &respMap)
|
||||||
|
return respMap["data"].([]any)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MarketNewsApi) ClsCalendar() []any {
|
||||||
|
url := "https://www.cls.cn/api/calendar/web/list?app=CailianpressWeb&flag=0&os=web&sv=8.4.6&type=0&sign=4b839750dc2f6b803d1c8ca00d2b40be"
|
||||||
|
resp, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R().
|
||||||
|
SetHeader("Host", "www.cls.cn").
|
||||||
|
SetHeader("Origin", "https://www.cls.cn").
|
||||||
|
SetHeader("Referer", "https://www.cls.cn/").
|
||||||
|
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("ClsCalendar err:%s", err.Error())
|
||||||
|
return []any{}
|
||||||
|
}
|
||||||
|
respMap := map[string]any{}
|
||||||
|
err = json.Unmarshal(resp.Body(), &respMap)
|
||||||
|
return respMap["data"].([]any)
|
||||||
|
}
|
||||||
|
@ -2,6 +2,7 @@ package data
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/coocood/freecache"
|
||||||
"go-stock/backend/db"
|
"go-stock/backend/db"
|
||||||
"go-stock/backend/logger"
|
"go-stock/backend/logger"
|
||||||
"testing"
|
"testing"
|
||||||
@ -36,7 +37,7 @@ func TestGetIndustryRank(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
func TestGetIndustryMoneyRankSina(t *testing.T) {
|
func TestGetIndustryMoneyRankSina(t *testing.T) {
|
||||||
res := NewMarketNewsApi().GetIndustryMoneyRankSina("2")
|
res := NewMarketNewsApi().GetIndustryMoneyRankSina("0", "netamount")
|
||||||
for i, re := range res {
|
for i, re := range res {
|
||||||
logger.SugaredLogger.Debugf("key: %+v, value: %+v", i, re)
|
logger.SugaredLogger.Debugf("key: %+v, value: %+v", i, re)
|
||||||
|
|
||||||
@ -46,7 +47,6 @@ func TestGetMoneyRankSina(t *testing.T) {
|
|||||||
res := NewMarketNewsApi().GetMoneyRankSina("r3_net")
|
res := NewMarketNewsApi().GetMoneyRankSina("r3_net")
|
||||||
for i, re := range res {
|
for i, re := range res {
|
||||||
logger.SugaredLogger.Debugf("key: %+v, value: %+v", i, re)
|
logger.SugaredLogger.Debugf("key: %+v, value: %+v", i, re)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,3 +56,98 @@ func TestGetStockMoneyTrendByDay(t *testing.T) {
|
|||||||
logger.SugaredLogger.Debugf("key: %+v, value: %+v", i, re)
|
logger.SugaredLogger.Debugf("key: %+v, value: %+v", i, re)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func TestTopStocksRankingList(t *testing.T) {
|
||||||
|
NewMarketNewsApi().TopStocksRankingList("2025-05-19")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLongTiger(t *testing.T) {
|
||||||
|
db.Init("../../data/stock.db")
|
||||||
|
|
||||||
|
NewMarketNewsApi().LongTiger("2025-06-08")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStockResearchReport(t *testing.T) {
|
||||||
|
db.Init("../../data/stock.db")
|
||||||
|
resp := NewMarketNewsApi().StockResearchReport("600584.sh", 7)
|
||||||
|
for _, a := range resp {
|
||||||
|
logger.SugaredLogger.Debugf("value: %+v", a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIndustryResearchReport(t *testing.T) {
|
||||||
|
db.Init("../../data/stock.db")
|
||||||
|
resp := NewMarketNewsApi().IndustryResearchReport("", 7)
|
||||||
|
for _, a := range resp {
|
||||||
|
logger.SugaredLogger.Debugf("value: %+v", a)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStockNotice(t *testing.T) {
|
||||||
|
db.Init("../../data/stock.db")
|
||||||
|
resp := NewMarketNewsApi().StockNotice("600584,600900")
|
||||||
|
for _, a := range resp {
|
||||||
|
logger.SugaredLogger.Debugf("value: %+v", a)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEMDictCode(t *testing.T) {
|
||||||
|
db.Init("../../data/stock.db")
|
||||||
|
resp := NewMarketNewsApi().EMDictCode("016", freecache.NewCache(100))
|
||||||
|
for _, a := range resp {
|
||||||
|
logger.SugaredLogger.Debugf("value: %+v", a)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTradingViewNews(t *testing.T) {
|
||||||
|
db.Init("../../data/stock.db")
|
||||||
|
resp := NewMarketNewsApi().TradingViewNews()
|
||||||
|
for _, a := range *resp {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvestCalendar(t *testing.T) {
|
||||||
|
db.Init("../../data/stock.db")
|
||||||
|
res := NewMarketNewsApi().InvestCalendar("2025-06")
|
||||||
|
for _, a := range res {
|
||||||
|
logger.SugaredLogger.Debugf("value: %+v", a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClsCalendar(t *testing.T) {
|
||||||
|
db.Init("../../data/stock.db")
|
||||||
|
res := NewMarketNewsApi().ClsCalendar()
|
||||||
|
for _, a := range res {
|
||||||
|
logger.SugaredLogger.Debugf("value: %+v", a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -161,7 +161,7 @@ func (o OpenAi) NewSummaryStockNewsStream(userQuestion string, sysPromptId *int)
|
|||||||
}()
|
}()
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
news := NewMarketNewsApi().GetNewsList("财联社电报", 100)
|
news := NewMarketNewsApi().GetNewsList("", 100)
|
||||||
messageText := strings.Builder{}
|
messageText := strings.Builder{}
|
||||||
for _, telegraph := range *news {
|
for _, telegraph := range *news {
|
||||||
messageText.WriteString("## " + telegraph.Time + ":" + "\n")
|
messageText.WriteString("## " + telegraph.Time + ":" + "\n")
|
||||||
|
@ -19,11 +19,12 @@ func TestNewDeepSeekOpenAiConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetTopNewsList(t *testing.T) {
|
func TestGetTopNewsList(t *testing.T) {
|
||||||
GetTopNewsList(30)
|
news := GetTopNewsList(30)
|
||||||
|
t.Log(news)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSearchGuShiTongStockInfo(t *testing.T) {
|
func TestSearchGuShiTongStockInfo(t *testing.T) {
|
||||||
//db.Init("../../data/stock.db")
|
db.Init("../../data/stock.db")
|
||||||
SearchGuShiTongStockInfo("hk01810", 60)
|
SearchGuShiTongStockInfo("hk01810", 60)
|
||||||
SearchGuShiTongStockInfo("sh600745", 60)
|
SearchGuShiTongStockInfo("sh600745", 60)
|
||||||
SearchGuShiTongStockInfo("gb_goog", 60)
|
SearchGuShiTongStockInfo("gb_goog", 60)
|
||||||
|
55
backend/data/search_stock_api.go
Normal file
55
backend/data/search_stock_api.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"go-stock/backend/logger"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Author spark
|
||||||
|
// @Date 2025/6/28 21:02
|
||||||
|
// @Desc
|
||||||
|
// -----------------------------------------------------------------------------------
|
||||||
|
type SearchStockApi struct {
|
||||||
|
words string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSearchStockApi(words string) *SearchStockApi {
|
||||||
|
return &SearchStockApi{words: words}
|
||||||
|
}
|
||||||
|
func (s SearchStockApi) SearchStock() map[string]any {
|
||||||
|
url := "https://np-tjxg-g.eastmoney.com/api/smart-tag/stock/v3/pw/search-code"
|
||||||
|
resp, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R().
|
||||||
|
SetHeader("Host", "np-tjxg-g.eastmoney.com").
|
||||||
|
SetHeader("Origin", "https://xuangu.eastmoney.com").
|
||||||
|
SetHeader("Referer", "https://xuangu.eastmoney.com/").
|
||||||
|
SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0").
|
||||||
|
SetHeader("Content-Type", "application/json").
|
||||||
|
SetBody(fmt.Sprintf(`{
|
||||||
|
"keyWord": "%s",
|
||||||
|
"pageSize": 50000,
|
||||||
|
"pageNo": 1,
|
||||||
|
"fingerprint": "e38b5faabf9378c8238e57219f0ebc9b",
|
||||||
|
"gids": [],
|
||||||
|
"matchWord": "",
|
||||||
|
"timestamp": "1751113883290349",
|
||||||
|
"shareToGuba": false,
|
||||||
|
"requestId": "8xTWgCDAjvQ5lmvz5mDA3Ydk2AE4yoiJ1751113883290",
|
||||||
|
"needCorrect": true,
|
||||||
|
"removedConditionIdList": [],
|
||||||
|
"xcId": "xc0af28549ab330013ed",
|
||||||
|
"ownSelectAll": false,
|
||||||
|
"dxInfo": [],
|
||||||
|
"extraCondition": ""
|
||||||
|
}`, s.words)).Post(url)
|
||||||
|
if err != nil {
|
||||||
|
logger.SugaredLogger.Errorf("SearchStock-err:%+v", err)
|
||||||
|
return map[string]any{}
|
||||||
|
}
|
||||||
|
respMap := map[string]any{}
|
||||||
|
json.Unmarshal(resp.Body(), &respMap)
|
||||||
|
//logger.SugaredLogger.Infof("resp:%+v", respMap["data"])
|
||||||
|
return respMap
|
||||||
|
}
|
25
backend/data/search_stock_api_test.go
Normal file
25
backend/data/search_stock_api_test.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go-stock/backend/db"
|
||||||
|
"go-stock/backend/logger"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSearchStock(t *testing.T) {
|
||||||
|
db.Init("../../data/stock.db")
|
||||||
|
|
||||||
|
res := NewSearchStockApi("算力股;净利润连续3年增长").SearchStock()
|
||||||
|
data := res["data"].(map[string]any)
|
||||||
|
result := data["result"].(map[string]any)
|
||||||
|
dataList := result["dataList"].([]any)
|
||||||
|
for _, v := range dataList {
|
||||||
|
d := v.(map[string]any)
|
||||||
|
logger.SugaredLogger.Infof("%s:%s", d["INDUSTRY"], d["SECURITY_SHORT_NAME"])
|
||||||
|
}
|
||||||
|
//columns := result["columns"].([]any)
|
||||||
|
//for _, v := range columns {
|
||||||
|
// logger.SugaredLogger.Infof("v:%+v", v)
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
@ -34,6 +34,7 @@ type Settings struct {
|
|||||||
DarkTheme bool `json:"darkTheme"`
|
DarkTheme bool `json:"darkTheme"`
|
||||||
BrowserPoolSize int `json:"browserPoolSize"`
|
BrowserPoolSize int `json:"browserPoolSize"`
|
||||||
EnableFund bool `json:"enableFund"`
|
EnableFund bool `json:"enableFund"`
|
||||||
|
EnablePushNews bool `json:"enablePushNews"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (receiver Settings) TableName() string {
|
func (receiver Settings) TableName() string {
|
||||||
@ -78,6 +79,7 @@ func (s SettingsApi) UpdateConfig() string {
|
|||||||
"enable_news": s.Config.EnableNews,
|
"enable_news": s.Config.EnableNews,
|
||||||
"dark_theme": s.Config.DarkTheme,
|
"dark_theme": s.Config.DarkTheme,
|
||||||
"enable_fund": s.Config.EnableFund,
|
"enable_fund": s.Config.EnableFund,
|
||||||
|
"enable_push_news": s.Config.EnablePushNews,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
logger.SugaredLogger.Infof("未找到配置,创建默认配置:%+v", s.Config)
|
logger.SugaredLogger.Infof("未找到配置,创建默认配置:%+v", s.Config)
|
||||||
@ -105,6 +107,7 @@ func (s SettingsApi) UpdateConfig() string {
|
|||||||
EnableNews: s.Config.EnableNews,
|
EnableNews: s.Config.EnableNews,
|
||||||
DarkTheme: s.Config.DarkTheme,
|
DarkTheme: s.Config.DarkTheme,
|
||||||
EnableFund: s.Config.EnableFund,
|
EnableFund: s.Config.EnableFund,
|
||||||
|
EnablePushNews: s.Config.EnablePushNews,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return "保存成功!"
|
return "保存成功!"
|
||||||
|
1
backend/data/stock_basic.json
Normal file
1
backend/data/stock_basic.json
Normal file
File diff suppressed because one or more lines are too long
@ -26,6 +26,7 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/plugin/soft_delete"
|
"gorm.io/plugin/soft_delete"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -247,7 +248,7 @@ func (receiver StockDataApi) GetIndexBasic() {
|
|||||||
func (receiver StockDataApi) GetStockBaseInfo() {
|
func (receiver StockDataApi) GetStockBaseInfo() {
|
||||||
res := &TushareStockBasicResponse{}
|
res := &TushareStockBasicResponse{}
|
||||||
fields := "ts_code,symbol,name,area,industry,cnspell,market,list_date,act_name,act_ent_type,fullname,exchange,list_status,curr_type,enname,delist_date,is_hs"
|
fields := "ts_code,symbol,name,area,industry,cnspell,market,list_date,act_name,act_ent_type,fullname,exchange,list_status,curr_type,enname,delist_date,is_hs"
|
||||||
_, err := receiver.client.R().
|
resp, err := receiver.client.R().
|
||||||
SetHeader("content-type", "application/json").
|
SetHeader("content-type", "application/json").
|
||||||
SetBody(&TushareRequest{
|
SetBody(&TushareRequest{
|
||||||
ApiName: "stock_basic",
|
ApiName: "stock_basic",
|
||||||
@ -258,8 +259,7 @@ func (receiver StockDataApi) GetStockBaseInfo() {
|
|||||||
SetResult(res).
|
SetResult(res).
|
||||||
Post(tushareApiUrl)
|
Post(tushareApiUrl)
|
||||||
//logger.SugaredLogger.Infof("GetStockBaseInfo %s", string(resp.Body()))
|
//logger.SugaredLogger.Infof("GetStockBaseInfo %s", string(resp.Body()))
|
||||||
//resp.Body()写入文件
|
ioutil.WriteFile("stock_basic.json", resp.Body(), 0666)
|
||||||
//ioutil.WriteFile("stock_basic.json", resp.Body(), 0666)
|
|
||||||
//logger.SugaredLogger.Infof("GetStockBaseInfo %+v", res)
|
//logger.SugaredLogger.Infof("GetStockBaseInfo %+v", res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.SugaredLogger.Error(err.Error())
|
logger.SugaredLogger.Error(err.Error())
|
||||||
@ -400,7 +400,20 @@ func (receiver StockDataApi) Follow(stockCode string) string {
|
|||||||
logger.SugaredLogger.Error(err)
|
logger.SugaredLogger.Error(err)
|
||||||
return "关注失败"
|
return "关注失败"
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(stockCode, "us") {
|
||||||
|
stockCode = strings.Replace(stockCode, "us", "gb_", 1)
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(stockCode, "US") {
|
||||||
|
stockCode = strings.Replace(stockCode, "US", "gb_", 1)
|
||||||
|
}
|
||||||
|
count := int64(0)
|
||||||
|
db.Dao.Model(&FollowedStock{}).Where("is_del = ?", 0).Count(&count)
|
||||||
|
logger.SugaredLogger.Errorf("Follow-count %v", count)
|
||||||
|
if count >= 63 {
|
||||||
|
return "最多只能关注63只股票"
|
||||||
|
}
|
||||||
|
|
||||||
|
stockCode = strings.ToLower(stockCode)
|
||||||
maxSort := int64(0)
|
maxSort := int64(0)
|
||||||
db.Dao.Model(&FollowedStock{}).Raw("select max(sort) as sort from followed_stock").Scan(&maxSort)
|
db.Dao.Model(&FollowedStock{}).Raw("select max(sort) as sort from followed_stock").Scan(&maxSort)
|
||||||
|
|
||||||
@ -463,15 +476,64 @@ func (receiver StockDataApi) SetAlarmChangePercent(val, alarmPrice float64, stoc
|
|||||||
return "设置成功"
|
return "设置成功"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (receiver StockDataApi) SetStockSort(sort int64, stockCode string) {
|
func (receiver StockDataApi) SetStockSort(newSort int64, stockCode string) {
|
||||||
if strutil.HasPrefixAny(stockCode, []string{"gb_"}) {
|
//if strutil.HasPrefixAny(stockCode, []string{"gb_"}) {
|
||||||
stockCode = strings.ToLower(stockCode)
|
// stockCode = strings.ToLower(stockCode)
|
||||||
stockCode = strings.Replace(stockCode, "gb_", "us", 1)
|
// stockCode = strings.Replace(stockCode, "gb_", "us", 1)
|
||||||
|
//}
|
||||||
|
|
||||||
|
// 获取当前排序值
|
||||||
|
var currentStock FollowedStock
|
||||||
|
if err := db.Dao.Model(&FollowedStock{}).Where("stock_code = ?", strings.ToLower(stockCode)).First(¤tStock).Error; err != nil {
|
||||||
|
logger.SugaredLogger.Error("找不到当前股票: ", err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
err := db.Dao.Model(&FollowedStock{}).Where("stock_code = ?", strings.ToLower(stockCode)).Update("sort", sort).Error
|
|
||||||
if err != nil {
|
oldSort := currentStock.Sort
|
||||||
logger.SugaredLogger.Error(err.Error())
|
|
||||||
|
// 如果排序值没有变化,直接返回
|
||||||
|
if oldSort == newSort {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
// 检查新排序位置是否被占用
|
||||||
|
var count int64
|
||||||
|
if err := db.Dao.Model(&FollowedStock{}).Where("sort = ?", newSort).Count(&count).Error; err != nil {
|
||||||
|
logger.SugaredLogger.Error("检查新排序位置被占用失败: ", err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if count == 0 {
|
||||||
|
// 新位置未被占用,直接更新当前记录
|
||||||
|
if err := db.Dao.Model(&FollowedStock{}).
|
||||||
|
Where("stock_code = ?", strings.ToLower(stockCode)).
|
||||||
|
Update("sort", newSort).Error; err != nil {
|
||||||
|
logger.SugaredLogger.Error("更新排序位置失败: ", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 新位置已被占用,需要移动其他记录
|
||||||
|
if newSort < oldSort {
|
||||||
|
// 向前移动:将中间记录向后移动
|
||||||
|
if err := db.Dao.Model(&FollowedStock{}).
|
||||||
|
Where("sort >= ? AND sort < ?", newSort, oldSort).
|
||||||
|
Update("sort", gorm.Expr("sort + 1")).Error; err != nil {
|
||||||
|
logger.SugaredLogger.Error("向前排序更新失败: ", err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 向后移动:将中间记录向前移动
|
||||||
|
if err := db.Dao.Model(&FollowedStock{}).
|
||||||
|
Where("sort > ? AND sort <= ?", oldSort, newSort).
|
||||||
|
Update("sort", gorm.Expr("sort - 1")).Error; err != nil {
|
||||||
|
logger.SugaredLogger.Error("向后排序更新失败: ", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新目标记录的排序
|
||||||
|
if err := db.Dao.Model(&FollowedStock{}).
|
||||||
|
Where("stock_code = ?", strings.ToLower(stockCode)).
|
||||||
|
Update("sort", newSort).Error; err != nil {
|
||||||
|
logger.SugaredLogger.Error("更新股票排序失败: ", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
func (receiver StockDataApi) SetStockAICron(cron string, stockCode string) {
|
func (receiver StockDataApi) SetStockAICron(cron string, stockCode string) {
|
||||||
if strutil.HasPrefixAny(stockCode, []string{"gb_"}) {
|
if strutil.HasPrefixAny(stockCode, []string{"gb_"}) {
|
||||||
|
@ -47,6 +47,7 @@ func TestGetFinancialReports(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestGetTelegraphSearch(t *testing.T) {
|
func TestGetTelegraphSearch(t *testing.T) {
|
||||||
|
db.Init("../../data/stock.db")
|
||||||
//url := "https://www.cls.cn/searchPage?keyword=%E9%97%BB%E6%B3%B0%E7%A7%91%E6%8A%80&type=telegram"
|
//url := "https://www.cls.cn/searchPage?keyword=%E9%97%BB%E6%B3%B0%E7%A7%91%E6%8A%80&type=telegram"
|
||||||
messages := SearchStockInfo("谷歌", "telegram", 30)
|
messages := SearchStockInfo("谷歌", "telegram", 30)
|
||||||
for _, message := range *messages {
|
for _, message := range *messages {
|
||||||
@ -56,16 +57,17 @@ func TestGetTelegraphSearch(t *testing.T) {
|
|||||||
//https://www.cls.cn/stock?code=sh600745
|
//https://www.cls.cn/stock?code=sh600745
|
||||||
}
|
}
|
||||||
func TestSearchStockInfoByCode(t *testing.T) {
|
func TestSearchStockInfoByCode(t *testing.T) {
|
||||||
|
db.Init("../../data/stock.db")
|
||||||
SearchStockInfoByCode("sh600745")
|
SearchStockInfoByCode("sh600745")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSearchStockPriceInfo(t *testing.T) {
|
func TestSearchStockPriceInfo(t *testing.T) {
|
||||||
db.Init("../../data/stock.db")
|
db.Init("../../data/stock.db")
|
||||||
//SearchStockPriceInfo("中信证券", "hk06030", 30)
|
//SearchStockPriceInfo("中信证券", "hk06030", 30)
|
||||||
//SearchStockPriceInfo("上海贝岭", "sh600171", 30)
|
SearchStockPriceInfo("上海贝岭", "sh600171", 30)
|
||||||
SearchStockPriceInfo("苹果公司", "gb_aapl", 30)
|
//SearchStockPriceInfo("苹果公司", "gb_aapl", 30)
|
||||||
//SearchStockPriceInfo("微创光电", "bj430198", 30)
|
//SearchStockPriceInfo("微创光电", "bj430198", 30)
|
||||||
getZSInfo("创业板指数", "sz399006", 30)
|
//getZSInfo("创业板指数", "sz399006", 30)
|
||||||
//getZSInfo("上证综合指数", "sh000001", 30)
|
//getZSInfo("上证综合指数", "sh000001", 30)
|
||||||
//getZSInfo("沪深300指数", "sh000300", 30)
|
//getZSInfo("沪深300指数", "sh000300", 30)
|
||||||
|
|
||||||
|
290
backend/data/stock_sentiment_analysis.go
Normal file
290
backend/data/stock_sentiment_analysis.go
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"github.com/go-ego/gse"
|
||||||
|
"go-stock/backend/logger"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 金融情感词典,包含股票市场相关的专业词汇
|
||||||
|
var (
|
||||||
|
seg gse.Segmenter
|
||||||
|
|
||||||
|
// 正面金融词汇及其权重
|
||||||
|
positiveFinanceWords = map[string]float64{
|
||||||
|
"上涨": 2.0, "涨停": 3.0, "牛市": 3.0, "反弹": 2.0, "新高": 2.5,
|
||||||
|
"利好": 2.5, "增持": 2.0, "买入": 2.0, "推荐": 1.5, "看多": 2.0,
|
||||||
|
"盈利": 2.0, "增长": 2.0, "超预期": 2.5, "强劲": 1.5, "回升": 1.5,
|
||||||
|
"复苏": 2.0, "突破": 2.0, "创新高": 3.0, "回暖": 1.5, "上扬": 1.5,
|
||||||
|
"利好消息": 3.0, "收益增长": 2.5, "利润增长": 2.5, "业绩优异": 2.5,
|
||||||
|
"潜力股": 2.0, "绩优股": 2.0, "强势": 1.5, "走高": 1.5, "攀升": 1.5,
|
||||||
|
"大涨": 2.5, "飙升": 3.0, "井喷": 3.0, "爆发": 2.5, "暴涨": 3.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 负面金融词汇及其权重
|
||||||
|
negativeFinanceWords = map[string]float64{
|
||||||
|
"下跌": 2.0, "跌停": 3.0, "熊市": 3.0, "回调": 1.5, "新低": 2.5,
|
||||||
|
"利空": 2.5, "减持": 2.0, "卖出": 2.0, "看空": 2.0, "亏损": 2.5,
|
||||||
|
"下滑": 2.0, "萎缩": 2.0, "不及预期": 2.5, "疲软": 1.5, "恶化": 2.0,
|
||||||
|
"衰退": 2.0, "跌破": 2.0, "创新低": 3.0, "走弱": 1.5, "下挫": 1.5,
|
||||||
|
"利空消息": 3.0, "收益下降": 2.5, "利润下滑": 2.5, "业绩不佳": 2.5,
|
||||||
|
"垃圾股": 2.0, "风险股": 2.0, "弱势": 1.5, "走低": 1.5, "缩量": 2.5,
|
||||||
|
"大跌": 2.5, "暴跌": 3.0, "崩盘": 3.0, "跳水": 3.0, "重挫": 3.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 否定词,用于反转情感极性
|
||||||
|
negationWords = map[string]struct{}{
|
||||||
|
"不": {}, "没": {}, "无": {}, "非": {}, "未": {}, "别": {}, "勿": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 程度副词,用于调整情感强度
|
||||||
|
degreeWords = map[string]float64{
|
||||||
|
"非常": 1.8, "极其": 2.2, "太": 1.8, "很": 1.5,
|
||||||
|
"比较": 0.8, "稍微": 0.6, "有点": 0.7, "显著": 1.5,
|
||||||
|
"大幅": 1.8, "急剧": 2.0, "轻微": 0.6, "小幅": 0.7,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转折词,用于识别情感转折
|
||||||
|
transitionWords = map[string]struct{}{
|
||||||
|
"但是": {}, "然而": {}, "不过": {}, "却": {}, "可是": {},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// 加载默认词典
|
||||||
|
err := seg.LoadDict()
|
||||||
|
if err != nil {
|
||||||
|
logger.SugaredLogger.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SentimentResult 情感分析结果类型
|
||||||
|
type SentimentResult struct {
|
||||||
|
Score float64 // 情感得分
|
||||||
|
Category SentimentType // 情感类别
|
||||||
|
PositiveCount int // 正面词数量
|
||||||
|
NegativeCount int // 负面词数量
|
||||||
|
Description string // 情感描述
|
||||||
|
}
|
||||||
|
|
||||||
|
// SentimentType 情感类型枚举
|
||||||
|
type SentimentType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Positive SentimentType = iota
|
||||||
|
Negative
|
||||||
|
Neutral
|
||||||
|
)
|
||||||
|
|
||||||
|
// AnalyzeSentiment 判断文本的情感
|
||||||
|
func AnalyzeSentiment(text string) SentimentResult {
|
||||||
|
// 初始化得分
|
||||||
|
score := 0.0
|
||||||
|
positiveCount := 0
|
||||||
|
negativeCount := 0
|
||||||
|
|
||||||
|
// 分词(简单按单个字符分割)
|
||||||
|
words := splitWords(text)
|
||||||
|
|
||||||
|
// 检查文本是否包含转折词,并分割成两部分
|
||||||
|
var transitionIndex int
|
||||||
|
var hasTransition bool
|
||||||
|
for i, word := range words {
|
||||||
|
if _, ok := transitionWords[word]; ok {
|
||||||
|
transitionIndex = i
|
||||||
|
hasTransition = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理有转折的文本
|
||||||
|
if hasTransition {
|
||||||
|
// 转折前的部分
|
||||||
|
preTransitionWords := words[:transitionIndex]
|
||||||
|
preScore, prePos, preNeg := calculateScore(preTransitionWords)
|
||||||
|
|
||||||
|
// 转折后的部分,权重加倍
|
||||||
|
postTransitionWords := words[transitionIndex+1:]
|
||||||
|
postScore, postPos, postNeg := calculateScore(postTransitionWords)
|
||||||
|
postScore *= 1.5 // 转折后的情感更重要
|
||||||
|
|
||||||
|
score = preScore + postScore
|
||||||
|
positiveCount = prePos + postPos
|
||||||
|
negativeCount = preNeg + postNeg
|
||||||
|
} else {
|
||||||
|
// 没有转折的文本
|
||||||
|
score, positiveCount, negativeCount = calculateScore(words)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确定情感类别
|
||||||
|
var category SentimentType
|
||||||
|
switch {
|
||||||
|
case score > 1.0:
|
||||||
|
category = Positive
|
||||||
|
case score < -1.0:
|
||||||
|
category = Negative
|
||||||
|
default:
|
||||||
|
category = Neutral
|
||||||
|
}
|
||||||
|
|
||||||
|
return SentimentResult{
|
||||||
|
Score: score,
|
||||||
|
Category: category,
|
||||||
|
PositiveCount: positiveCount,
|
||||||
|
NegativeCount: negativeCount,
|
||||||
|
Description: GetSentimentDescription(category),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算情感得分
|
||||||
|
func calculateScore(words []string) (float64, int, int) {
|
||||||
|
score := 0.0
|
||||||
|
positiveCount := 0
|
||||||
|
negativeCount := 0
|
||||||
|
|
||||||
|
// 遍历每个词,计算情感得分
|
||||||
|
for i, word := range words {
|
||||||
|
// 首先检查是否为程度副词
|
||||||
|
degree, isDegree := degreeWords[word]
|
||||||
|
|
||||||
|
// 检查是否为否定词
|
||||||
|
_, isNegation := negationWords[word]
|
||||||
|
|
||||||
|
// 检查是否为金融正面词
|
||||||
|
if posScore, isPositive := positiveFinanceWords[word]; isPositive {
|
||||||
|
// 检查前一个词是否为否定词或程度副词
|
||||||
|
if i > 0 {
|
||||||
|
prevWord := words[i-1]
|
||||||
|
if _, isNeg := negationWords[prevWord]; isNeg {
|
||||||
|
score -= posScore
|
||||||
|
negativeCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if deg, isDeg := degreeWords[prevWord]; isDeg {
|
||||||
|
score += posScore * deg
|
||||||
|
positiveCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
score += posScore
|
||||||
|
positiveCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否为金融负面词
|
||||||
|
if negScore, isNegative := negativeFinanceWords[word]; isNegative {
|
||||||
|
// 检查前一个词是否为否定词或程度副词
|
||||||
|
if i > 0 {
|
||||||
|
prevWord := words[i-1]
|
||||||
|
if _, isNeg := negationWords[prevWord]; isNeg {
|
||||||
|
score += negScore
|
||||||
|
positiveCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if deg, isDeg := degreeWords[prevWord]; isDeg {
|
||||||
|
score -= negScore * deg
|
||||||
|
negativeCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
score -= negScore
|
||||||
|
negativeCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理程度副词(如果后面跟着情感词)
|
||||||
|
if isDegree && i+1 < len(words) {
|
||||||
|
nextWord := words[i+1]
|
||||||
|
|
||||||
|
if posScore, isPositive := positiveFinanceWords[nextWord]; isPositive {
|
||||||
|
score += posScore * degree
|
||||||
|
positiveCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if negScore, isNegative := negativeFinanceWords[nextWord]; isNegative {
|
||||||
|
score -= negScore * degree
|
||||||
|
negativeCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理否定词(如果后面跟着情感词)
|
||||||
|
if isNegation && i+1 < len(words) {
|
||||||
|
nextWord := words[i+1]
|
||||||
|
|
||||||
|
if posScore, isPositive := positiveFinanceWords[nextWord]; isPositive {
|
||||||
|
score -= posScore
|
||||||
|
negativeCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if negScore, isNegative := negativeFinanceWords[nextWord]; isNegative {
|
||||||
|
score += negScore
|
||||||
|
positiveCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return score, positiveCount, negativeCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// 简单的分词函数,考虑了中文和英文
|
||||||
|
func splitWords(text string) []string {
|
||||||
|
return seg.Cut(text, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSentimentDescription 获取情感类别的文本描述
|
||||||
|
func GetSentimentDescription(category SentimentType) string {
|
||||||
|
switch category {
|
||||||
|
case Positive:
|
||||||
|
return "看涨"
|
||||||
|
case Negative:
|
||||||
|
return "看跌"
|
||||||
|
case Neutral:
|
||||||
|
return "中性"
|
||||||
|
default:
|
||||||
|
return "未知"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 从命令行读取输入
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
fmt.Println("请输入要分析的股市相关文本(输入exit退出):")
|
||||||
|
|
||||||
|
for {
|
||||||
|
fmt.Print("> ")
|
||||||
|
text, err := reader.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("读取输入时出错:", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去除换行符
|
||||||
|
text = strings.TrimSpace(text)
|
||||||
|
|
||||||
|
// 检查是否退出
|
||||||
|
if text == "exit" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分析情感
|
||||||
|
result := AnalyzeSentiment(text)
|
||||||
|
|
||||||
|
// 输出结果
|
||||||
|
fmt.Printf("情感分析结果: %s (得分: %.2f, 正面词:%d, 负面词:%d)\n",
|
||||||
|
GetSentimentDescription(result.Category),
|
||||||
|
result.Score,
|
||||||
|
result.PositiveCount,
|
||||||
|
result.NegativeCount)
|
||||||
|
}
|
||||||
|
}
|
36
backend/data/stock_sentiment_analysis_test.go
Normal file
36
backend/data/stock_sentiment_analysis_test.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Author spark
|
||||||
|
// @Date 2025/6/19 13:05
|
||||||
|
// @Desc
|
||||||
|
//-----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func TestAnalyzeSentiment(t *testing.T) {
|
||||||
|
// 分析情感
|
||||||
|
text := " 【调查:韩国近两成中小学生过度使用智能手机或互联网】财联社6月19日电,韩国女性家族部18日公布的一项年度调查结果显示,接受调查的韩国中小学生中,共计约17.3%、即超过21万人使用智能手机或互联网的程度达到了“危险水平”,这意味着他们因过度依赖智能手机或互联网而需要关注或干预,这一比例引人担忧。 (新华社)\n"
|
||||||
|
text = "消息人士称,联合利华(Unilever)正在为Graze零食品牌寻找买家。\n"
|
||||||
|
text = "【韩国未来5年将投入51万亿韩元发展文化产业】 据韩联社,韩国文化体育观光部(文体部)今后5年将投入51万亿韩元(约合人民币2667亿元)预算,落实总统李在明在竞选时期提出的“将韩国打造成全球五大文化强国之一”的承诺。\n"
|
||||||
|
//text = "【油气股持续拉升 国际实业午后涨停】财联社6月19日电,油气股午后持续拉升,国际实业、宝莫股份午后涨停,准油股份、山东墨龙。茂化实华此前涨停,通源石油、海默科技、贝肯能源、中曼石油、科力股份等多股涨超5%。\n"
|
||||||
|
//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)
|
||||||
|
|
||||||
|
// 输出结果
|
||||||
|
fmt.Printf("情感分析结果: %s (得分: %.2f, 正面词:%d, 负面词:%d)\n",
|
||||||
|
result.Description,
|
||||||
|
result.Score,
|
||||||
|
result.PositiveCount,
|
||||||
|
result.NegativeCount)
|
||||||
|
|
||||||
|
}
|
@ -218,14 +218,15 @@ type Prompt struct {
|
|||||||
|
|
||||||
type Telegraph struct {
|
type Telegraph struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
Time string `json:"time"`
|
Time string `json:"time"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
SubjectTags []string `json:"subjects" gorm:"-:all"`
|
SubjectTags []string `json:"subjects" gorm:"-:all"`
|
||||||
StocksTags []string `json:"stocks" gorm:"-:all"`
|
StocksTags []string `json:"stocks" gorm:"-:all"`
|
||||||
IsRed bool `json:"isRed"`
|
IsRed bool `json:"isRed"`
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
Source string `json:"source"`
|
Source string `json:"source"`
|
||||||
TelegraphTags []TelegraphTags `json:"tags" gorm:"-:migration;foreignKey:TelegraphId"`
|
TelegraphTags []TelegraphTags `json:"tags" gorm:"-:migration;foreignKey:TelegraphId"`
|
||||||
|
SentimentResult string `json:"sentimentResult" gorm:"-:all"`
|
||||||
}
|
}
|
||||||
type TelegraphTags struct {
|
type TelegraphTags struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
@ -277,3 +278,87 @@ type SinaStockInfo struct {
|
|||||||
MarketValue string `json:"market_value"`
|
MarketValue string `json:"market_value"`
|
||||||
PeRatio string `json:"pe_ratio"`
|
PeRatio string `json:"pe_ratio"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LongTigerRankData struct {
|
||||||
|
ACCUMAMOUNT float64 `json:"ACCUM_AMOUNT"`
|
||||||
|
BILLBOARDBUYAMT float64 `json:"BILLBOARD_BUY_AMT"`
|
||||||
|
BILLBOARDDEALAMT float64 `json:"BILLBOARD_DEAL_AMT"`
|
||||||
|
BILLBOARDNETAMT float64 `json:"BILLBOARD_NET_AMT"`
|
||||||
|
BILLBOARDSELLAMT float64 `json:"BILLBOARD_SELL_AMT"`
|
||||||
|
CHANGERATE float64 `json:"CHANGE_RATE"`
|
||||||
|
CLOSEPRICE float64 `json:"CLOSE_PRICE"`
|
||||||
|
DEALAMOUNTRATIO float64 `json:"DEAL_AMOUNT_RATIO"`
|
||||||
|
DEALNETRATIO float64 `json:"DEAL_NET_RATIO"`
|
||||||
|
EXPLAIN string `json:"EXPLAIN"`
|
||||||
|
EXPLANATION string `json:"EXPLANATION"`
|
||||||
|
FREEMARKETCAP float64 `json:"FREE_MARKET_CAP"`
|
||||||
|
SECUCODE string `json:"SECUCODE" gorm:"index"`
|
||||||
|
SECURITYCODE string `json:"SECURITY_CODE"`
|
||||||
|
SECURITYNAMEABBR string `json:"SECURITY_NAME_ABBR"`
|
||||||
|
SECURITYTYPECODE string `json:"SECURITY_TYPE_CODE"`
|
||||||
|
TRADEDATE string `json:"TRADE_DATE" gorm:"index"`
|
||||||
|
TURNOVERRATE float64 `json:"TURNOVERRATE"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
681
frontend/package-lock.json
generated
681
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,7 @@
|
|||||||
"@types/file-saver": "^2.0.7",
|
"@types/file-saver": "^2.0.7",
|
||||||
"@vavt/cm-extension": "^1.8.0",
|
"@vavt/cm-extension": "^1.8.0",
|
||||||
"@vavt/v3-extension": "^3.0.0",
|
"@vavt/v3-extension": "^3.0.0",
|
||||||
"@vicons/ionicons5": "^0.13.0",
|
"date-fns": "^4.1.0",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
@ -23,11 +23,19 @@
|
|||||||
"vue3-danmaku": "^1.6.1"
|
"vue3-danmaku": "^1.6.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@vicons/antd": "^0.13.0",
|
||||||
|
"@vicons/carbon": "^0.13.0",
|
||||||
|
"@vicons/fa": "^0.13.0",
|
||||||
|
"@vicons/fluent": "^0.13.0",
|
||||||
|
"@vicons/ionicons4": "^0.13.0",
|
||||||
|
"@vicons/ionicons5": "^0.13.0",
|
||||||
|
"@vicons/material": "^0.13.0",
|
||||||
|
"@vicons/tabler": "^0.13.0",
|
||||||
"@vitejs/plugin-vue": "^5.2.1",
|
"@vitejs/plugin-vue": "^5.2.1",
|
||||||
"html-docx-js-typescript": "^0.1.5",
|
"html-docx-js-typescript": "^0.1.5",
|
||||||
"naive-ui": "^2.41.0",
|
"naive-ui": "^2.41.0",
|
||||||
"vfonts": "^0.0.3",
|
"vfonts": "^0.0.3",
|
||||||
"vite": "^5.4.12"
|
"vite": "^6.3.5"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"AI赋能股票分析",
|
"AI赋能股票分析",
|
||||||
|
@ -1 +1 @@
|
|||||||
99aeae4d0e7cbe900b379d3e7d2f44d7
|
2d63c3a999d797889c01d6c96451b197
|
@ -10,24 +10,32 @@ import {
|
|||||||
} from '../wailsjs/runtime'
|
} from '../wailsjs/runtime'
|
||||||
import {h, onBeforeMount, onBeforeUnmount, onMounted, ref} from "vue";
|
import {h, onBeforeMount, onBeforeUnmount, onMounted, ref} from "vue";
|
||||||
import {RouterLink, useRouter} from 'vue-router'
|
import {RouterLink, useRouter} from 'vue-router'
|
||||||
import {darkTheme, NIcon, NText,} from 'naive-ui'
|
import {createDiscreteApi,darkTheme,lightTheme , NIcon, NText,dateZhCN,zhCN} from 'naive-ui'
|
||||||
import {
|
import {
|
||||||
AlarmOutline,
|
AlarmOutline,
|
||||||
AnalyticsOutline,
|
AnalyticsOutline,
|
||||||
BarChartSharp,
|
BarChartSharp, Bonfire, BonfireOutline, EaselSharp,
|
||||||
ExpandOutline,
|
ExpandOutline, Flag,
|
||||||
Flame,
|
Flame, FlameSharp, InformationOutline,
|
||||||
LogoGithub,
|
LogoGithub,
|
||||||
NewspaperOutline,
|
NewspaperOutline,
|
||||||
NewspaperSharp,
|
NewspaperSharp, Notifications,
|
||||||
PowerOutline,
|
PowerOutline, Pulse,
|
||||||
ReorderTwoOutline,
|
ReorderTwoOutline,
|
||||||
SettingsOutline,
|
SettingsOutline, Skull, SkullOutline, SkullSharp,
|
||||||
SparklesOutline,
|
SparklesOutline,
|
||||||
StarOutline,
|
StarOutline,
|
||||||
Wallet,
|
Wallet, WarningOutline,
|
||||||
} from '@vicons/ionicons5'
|
} from '@vicons/ionicons5'
|
||||||
import {GetConfig, GetGroupList} from "../wailsjs/go/main/App";
|
import {AnalyzeSentiment, GetConfig, GetGroupList} from "../wailsjs/go/main/App";
|
||||||
|
import {Dragon, Fire, Gripfire} from "@vicons/fa";
|
||||||
|
import {ReportSearch} from "@vicons/tabler";
|
||||||
|
import {LocalFireDepartmentRound} from "@vicons/material";
|
||||||
|
import {BoxSearch20Regular, CommentNote20Filled} from "@vicons/fluent";
|
||||||
|
import {FireFilled, FireOutlined, NotificationFilled, StockOutlined} from "@vicons/antd";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
@ -36,7 +44,7 @@ const enableNews = ref(false)
|
|||||||
const contentStyle = ref("")
|
const contentStyle = ref("")
|
||||||
const enableFund = ref(false)
|
const enableFund = ref(false)
|
||||||
const enableDarkTheme = ref(null)
|
const enableDarkTheme = ref(null)
|
||||||
const content = ref('数据来源于网络,仅供参考;投资有风险,入市需谨慎')
|
const content = ref('数据来源于网络,仅供参考;投资有风险,入市需谨慎\n\n未经授权,禁止商业目的!')
|
||||||
const isFullscreen = ref(false)
|
const isFullscreen = ref(false)
|
||||||
const activeKey = ref('')
|
const activeKey = ref('')
|
||||||
const containerRef = ref({})
|
const containerRef = ref({})
|
||||||
@ -196,7 +204,7 @@ const menuOptions = ref([
|
|||||||
{default: () => '行业排名',}
|
{default: () => '行业排名',}
|
||||||
),
|
),
|
||||||
key: 'market4',
|
key: 'market4',
|
||||||
icon: renderIcon(Flame),
|
icon: renderIcon(Flag),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: () =>
|
label: () =>
|
||||||
@ -217,8 +225,134 @@ const menuOptions = ref([
|
|||||||
{default: () => '个股资金流向',}
|
{default: () => '个股资金流向',}
|
||||||
),
|
),
|
||||||
key: 'market5',
|
key: 'market5',
|
||||||
icon: renderIcon(Wallet),
|
icon: renderIcon(Pulse),
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
RouterLink,
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
to: {
|
||||||
|
name: 'market',
|
||||||
|
query: {
|
||||||
|
name: "龙虎榜",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClick: () => {
|
||||||
|
EventsEmit("changeMarketTab", {ID: 0, name: '龙虎榜'})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{default: () => '龙虎榜',}
|
||||||
|
),
|
||||||
|
key: 'market6',
|
||||||
|
icon: renderIcon(Dragon),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
RouterLink,
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
to: {
|
||||||
|
name: 'market',
|
||||||
|
query: {
|
||||||
|
name: "个股研报",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClick: () => {
|
||||||
|
EventsEmit("changeMarketTab", {ID: 0, name: '个股研报'})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{default: () => '个股研报',}
|
||||||
|
),
|
||||||
|
key: 'market7',
|
||||||
|
icon: renderIcon(StockOutlined),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
RouterLink,
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
to: {
|
||||||
|
name: 'market',
|
||||||
|
query: {
|
||||||
|
name: "公司公告",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClick: () => {
|
||||||
|
EventsEmit("changeMarketTab", {ID: 0, name: '公司公告'})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{default: () => '公司公告',}
|
||||||
|
),
|
||||||
|
key: 'market8',
|
||||||
|
icon: renderIcon(NotificationFilled),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
RouterLink,
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
to: {
|
||||||
|
name: 'market',
|
||||||
|
query: {
|
||||||
|
name: "行业研究",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClick: () => {
|
||||||
|
EventsEmit("changeMarketTab", {ID: 0, name: '行业研究'})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{default: () => '行业研究',}
|
||||||
|
),
|
||||||
|
key: 'market9',
|
||||||
|
icon: renderIcon(ReportSearch),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
RouterLink,
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
to: {
|
||||||
|
name: 'market',
|
||||||
|
query: {
|
||||||
|
name: "当前热门",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClick: () => {
|
||||||
|
EventsEmit("changeMarketTab", {ID: 0, name: '当前热门'})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{default: () => '当前热门',}
|
||||||
|
),
|
||||||
|
key: 'market10',
|
||||||
|
icon: renderIcon(Gripfire),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
RouterLink,
|
||||||
|
{
|
||||||
|
href: '#',
|
||||||
|
to: {
|
||||||
|
name: 'market',
|
||||||
|
query: {
|
||||||
|
name: "指标选股",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClick: () => {
|
||||||
|
EventsEmit("changeMarketTab", {ID: 0, name: '指标选股'})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{default: () => '指标选股',}
|
||||||
|
),
|
||||||
|
key: 'market11',
|
||||||
|
icon: renderIcon(BoxSearch20Regular),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -367,6 +501,7 @@ onBeforeUnmount(() => {
|
|||||||
EventsOff("realtime_profit")
|
EventsOff("realtime_profit")
|
||||||
EventsOff("loadingMsg")
|
EventsOff("loadingMsg")
|
||||||
EventsOff("telegraph")
|
EventsOff("telegraph")
|
||||||
|
EventsOff("newsPush")
|
||||||
})
|
})
|
||||||
|
|
||||||
window.onerror = function (msg, source, lineno, colno, error) {
|
window.onerror = function (msg, source, lineno, colno, error) {
|
||||||
@ -452,12 +587,39 @@ onMounted(() => {
|
|||||||
enableNews.value = true
|
enableNews.value = true
|
||||||
}
|
}
|
||||||
enableFund.value = res.enableFund
|
enableFund.value = res.enableFund
|
||||||
|
const {notification } =createDiscreteApi(["notification"], {
|
||||||
|
configProviderProps: {
|
||||||
|
theme: enableDarkTheme.value ? darkTheme : lightTheme ,
|
||||||
|
max: 3,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
EventsOn("newsPush", (data) => {
|
||||||
|
//console.log(data)
|
||||||
|
if(data.isRed){
|
||||||
|
notification.create({
|
||||||
|
//type:"error",
|
||||||
|
// avatar: () => h(NIcon,{component:Notifications,color:"red"}),
|
||||||
|
title: data.time,
|
||||||
|
content: () => h(NText,{type:"error"}, { default: () => data.content }),
|
||||||
|
meta: () => h(NText,{type:"warning"}, { default: () => data.source}),
|
||||||
|
duration:1000*40,
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
notification.create({
|
||||||
|
//type:"info",
|
||||||
|
//avatar: () => h(NIcon,{component:Notifications}),
|
||||||
|
title: data.time,
|
||||||
|
content: () => h(NText,{type:"info"}, { default: () => data.content }),
|
||||||
|
meta: () => h(NText,{type:"warning"}, { default: () => data.source}),
|
||||||
|
duration:1000*30 ,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<n-config-provider ref="containerRef" :theme="enableDarkTheme">
|
<n-config-provider ref="containerRef" :theme="enableDarkTheme" :locale="zhCN" :date-locale="dateZhCN">
|
||||||
<n-message-provider>
|
<n-message-provider>
|
||||||
<n-notification-provider>
|
<n-notification-provider>
|
||||||
<n-modal-provider>
|
<n-modal-provider>
|
||||||
|
102
frontend/src/components/ClsCalendarTimeLine.vue
Normal file
102
frontend/src/components/ClsCalendarTimeLine.vue
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {nextTick, onBeforeMount, onMounted, onUnmounted, ref} from 'vue'
|
||||||
|
import {ClsCalendar} from "../../wailsjs/go/main/App";
|
||||||
|
import { addMonths, format ,parse} from 'date-fns';
|
||||||
|
import { zhCN } from 'date-fns/locale';
|
||||||
|
|
||||||
|
import {useMessage} from 'naive-ui'
|
||||||
|
import {Star48Filled} from "@vicons/fluent";
|
||||||
|
const today = new Date();
|
||||||
|
const year = today.getFullYear();
|
||||||
|
const month = String(today.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要+1
|
||||||
|
const day = String(today.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
|
// 常见格式:YYYY-MM-DD
|
||||||
|
const formattedDate = `${year}-${month}-${day}`;
|
||||||
|
const formattedYM = `${year}-${month}`;
|
||||||
|
const list = ref([])
|
||||||
|
const message=useMessage()
|
||||||
|
|
||||||
|
function goBackToday() {
|
||||||
|
setTimeout(() => {
|
||||||
|
nextTick(
|
||||||
|
() => {
|
||||||
|
const elementById = document.getElementById(formattedDate);
|
||||||
|
if (elementById) {
|
||||||
|
elementById.scrollIntoView({
|
||||||
|
behavior: 'auto',
|
||||||
|
block: 'start'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
ClsCalendar().then(res => {
|
||||||
|
list.value = res
|
||||||
|
goBackToday();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
function getweekday(date){
|
||||||
|
let day=parse(date, 'yyyy-MM-dd', new Date())
|
||||||
|
return format(day, 'EEEE', {locale: zhCN})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- <n-timeline size="large" style="text-align: left">-->
|
||||||
|
<!-- <n-timeline-item v-for="item in list" :key="item.date" :title="item.date" type="info" >-->
|
||||||
|
<!-- <n-list>-->
|
||||||
|
<!-- <n-list-item v-for="l in item.list" :key="l.article_id ">-->
|
||||||
|
<!-- <n-text>{{l.title}}</n-text>-->
|
||||||
|
<!-- </n-list-item>-->
|
||||||
|
<!-- </n-list>-->
|
||||||
|
<!-- </n-timeline-item>-->
|
||||||
|
<!-- </n-timeline>-->
|
||||||
|
|
||||||
|
<n-list bordered style="max-height: calc(100vh - 230px);text-align: left;">
|
||||||
|
<n-scrollbar style="max-height: calc(100vh - 230px);" >
|
||||||
|
<n-list-item v-for="(item, index) in list" :id="item.calendar_day" :key="item.calendar_day">
|
||||||
|
<n-thing :title="item.calendar_day +' '+item.week">
|
||||||
|
<n-list :bordered="false" hoverable>
|
||||||
|
<n-list-item v-for="(l,i ) in item.items" :key="l.id ">
|
||||||
|
<n-flex justify="space-between">
|
||||||
|
<n-text :type="item.calendar_day===formattedDate?'warning':'info'">{{i+1}}# {{l.title}}
|
||||||
|
<n-tag v-if="l.event" size="small" round type="success">事件</n-tag>
|
||||||
|
<n-tag v-if="l.economic" size="small" round type="error">数据</n-tag>
|
||||||
|
</n-text>
|
||||||
|
<n-rate v-if="l.event&&(l.event.star>0)" readonly :default-value="l.event.star">
|
||||||
|
<n-icon :component="Star48Filled"/>
|
||||||
|
</n-rate>
|
||||||
|
<n-rate v-if="l.economic&&(l.economic.star>0)" readonly :default-value="l.economic.star" >
|
||||||
|
<n-icon :component="Star48Filled"/>
|
||||||
|
</n-rate>
|
||||||
|
</n-flex>
|
||||||
|
|
||||||
|
<n-flex v-if="l.economic">
|
||||||
|
<n-tag type="warning" :bordered="false" :size="'small'">公布:{{l.economic.actual }}</n-tag>
|
||||||
|
<n-tag type="warning" :bordered="false" :size="'small'">预测:{{l.economic.consensus}}</n-tag>
|
||||||
|
<n-tag type="warning" :bordered="false" :size="'small'">前值:{{l.economic.front}}</n-tag>
|
||||||
|
</n-flex>
|
||||||
|
</n-list-item>
|
||||||
|
</n-list>
|
||||||
|
</n-thing>
|
||||||
|
</n-list-item>
|
||||||
|
<n-list-item v-if="list.length==0">
|
||||||
|
<n-text type="info">没有数据</n-text>
|
||||||
|
</n-list-item>
|
||||||
|
<n-list-item v-else style="text-align: center;">
|
||||||
|
<n-button-group>
|
||||||
|
<n-button strong secondary type="warning" @click="goBackToday">回到今天</n-button>
|
||||||
|
</n-button-group>
|
||||||
|
</n-list-item>
|
||||||
|
</n-scrollbar>
|
||||||
|
</n-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
37
frontend/src/components/HotEvents.vue
Normal file
37
frontend/src/components/HotEvents.vue
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {onBeforeMount, onUnmounted, ref} from 'vue'
|
||||||
|
import {HotEvent} from "../../wailsjs/go/main/App";
|
||||||
|
const list = ref([])
|
||||||
|
|
||||||
|
const task =ref()
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
list.value = await HotEvent(50)
|
||||||
|
task.value=setInterval(async ()=>{
|
||||||
|
list.value = await HotEvent(50)
|
||||||
|
}, 1000*10)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(async ()=>{
|
||||||
|
clearInterval(task.value)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-list bordered>
|
||||||
|
<template #header>
|
||||||
|
雪球热门
|
||||||
|
</template>
|
||||||
|
<n-list-item v-for="(item, index) in list" :key="index">
|
||||||
|
<n-thing :title="item.tag" :description="item.content" >
|
||||||
|
<template v-if="item.pic" #avatar>
|
||||||
|
<n-avatar :src="item.pic" :size="60">
|
||||||
|
</n-avatar>
|
||||||
|
</template>
|
||||||
|
</n-thing>
|
||||||
|
</n-list-item>
|
||||||
|
</n-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
88
frontend/src/components/HotStockList.vue
Normal file
88
frontend/src/components/HotStockList.vue
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {onBeforeMount, onUnmounted, ref} from 'vue'
|
||||||
|
import {HotStock} from "../../wailsjs/go/main/App";
|
||||||
|
import KLineChart from "./KLineChart.vue";
|
||||||
|
import {ArrowBack, ArrowDown, ArrowUp} from "@vicons/ionicons5";
|
||||||
|
|
||||||
|
const {marketType}=defineProps(
|
||||||
|
{
|
||||||
|
marketType: {
|
||||||
|
type: String,
|
||||||
|
default: '10'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
const task =ref()
|
||||||
|
|
||||||
|
const list = ref([])
|
||||||
|
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
list.value = await HotStock(marketType)
|
||||||
|
task.value = setInterval(async () => {
|
||||||
|
list.value = await HotStock(marketType)
|
||||||
|
}, 5000)
|
||||||
|
})
|
||||||
|
onUnmounted(()=>{
|
||||||
|
clearInterval(task.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
function getMarketCode(item) {
|
||||||
|
if (item.exchange === 'SZ') {
|
||||||
|
return item.code.toLowerCase()
|
||||||
|
}
|
||||||
|
if (item.exchange === 'SH') {
|
||||||
|
return item.code.toLowerCase()
|
||||||
|
}
|
||||||
|
if (item.exchange === 'HK') {
|
||||||
|
return (item.exchange + item.code).toLowerCase()
|
||||||
|
}
|
||||||
|
return ("gb_"+item.code).toLowerCase()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-table striped size="small">
|
||||||
|
<n-thead>
|
||||||
|
<n-tr>
|
||||||
|
<n-th>股票名称</n-th>
|
||||||
|
<n-th>涨跌幅</n-th>
|
||||||
|
<n-th>当前价格</n-th>
|
||||||
|
<n-th>热度</n-th>
|
||||||
|
<n-th>热度变化</n-th>
|
||||||
|
<n-th>排名变化</n-th>
|
||||||
|
</n-tr>
|
||||||
|
</n-thead>
|
||||||
|
<n-tbody>
|
||||||
|
<n-tr v-for="item in list" :key="item.code">
|
||||||
|
<n-td><n-text type="info">
|
||||||
|
<n-popover trigger="hover" placement="right">
|
||||||
|
<template #trigger>
|
||||||
|
<n-tag type="info" :bordered="false"> {{item.name}} {{item.code}}</n-tag>
|
||||||
|
</template>
|
||||||
|
<k-line-chart style="width: 800px" :code="getMarketCode(item)" :chart-height="500" :name="item.name" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||||
|
</n-popover>
|
||||||
|
</n-text></n-td>
|
||||||
|
<n-td><n-text :type="item.percent>0?'error':'success'">{{item.percent}}%</n-text></n-td>
|
||||||
|
<n-td><n-text type="info">{{item.current}}</n-text></n-td>
|
||||||
|
<n-td><n-text type="info">{{item.value}}</n-text></n-td>
|
||||||
|
<n-td><n-text :type="item.increment>0?'error':'success'">
|
||||||
|
{{item.increment}}
|
||||||
|
<n-icon v-if="item.increment>0" :component="ArrowUp"/>
|
||||||
|
<n-icon v-else :component="ArrowDown"/>
|
||||||
|
</n-text></n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.rank_change>0?'error':'success'">
|
||||||
|
{{item.rank_change}}
|
||||||
|
<n-icon v-if="item.rank_change>0" :component="ArrowUp"/>
|
||||||
|
<n-text v-else-if="item.rank_change==0" ></n-text>
|
||||||
|
<n-icon v-else :component="ArrowDown"/>
|
||||||
|
</n-text>
|
||||||
|
</n-td>
|
||||||
|
</n-tr>
|
||||||
|
</n-tbody>
|
||||||
|
</n-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
73
frontend/src/components/HotTopics.vue
Normal file
73
frontend/src/components/HotTopics.vue
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {onBeforeMount, onUnmounted, ref} from 'vue'
|
||||||
|
import {HotTopic} from "../../wailsjs/go/main/App";
|
||||||
|
const list = ref([])
|
||||||
|
const task =ref()
|
||||||
|
|
||||||
|
onBeforeMount(async () => {
|
||||||
|
list.value = await HotTopic(10)
|
||||||
|
setInterval(async ()=>{
|
||||||
|
list.value = await HotTopic(10)
|
||||||
|
}, 1000*10)
|
||||||
|
})
|
||||||
|
onUnmounted(()=>{
|
||||||
|
clearInterval(task.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
function openCenteredWindow(url, width, height) {
|
||||||
|
const left = (window.screen.width - width) / 2;
|
||||||
|
const top = (window.screen.height - height) / 2;
|
||||||
|
|
||||||
|
return window.open(
|
||||||
|
url,
|
||||||
|
'centeredWindow',
|
||||||
|
`width=${width},height=${height},left=${left},top=${top}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
function showPage(htid) {
|
||||||
|
openCenteredWindow(`https://gubatopic.eastmoney.com/topic_v3.html?htid=${htid}`, 1000, 600)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-list bordered hoverable clickable>
|
||||||
|
<!-- <template #header>-->
|
||||||
|
<!-- 股吧热门-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<n-list-item v-for="(item, index) in list" :key="index">
|
||||||
|
<n-thing :title="item.nickname" :description="item.desc" :description-style="'font-size: 14px;'" @click="showPage(item.htid)">
|
||||||
|
<template v-if="item.squareImg" #avatar>
|
||||||
|
<n-avatar :src="item.squareImg" :size="60">
|
||||||
|
</n-avatar>
|
||||||
|
</template>
|
||||||
|
<template v-if="item.stock_list" #footer>
|
||||||
|
<n-flex>
|
||||||
|
<n-tag type="info" v-for="(v, i) in item.stock_list" :bordered="false" size="small">
|
||||||
|
{{v.name}}
|
||||||
|
</n-tag>
|
||||||
|
</n-flex>
|
||||||
|
</template>
|
||||||
|
<template v-if="item.clickNumber" #header-extra>
|
||||||
|
<n-flex>
|
||||||
|
<n-button secondary type="warning" size="tiny">讨论数:<n-number-animation
|
||||||
|
show-separator
|
||||||
|
:from="0"
|
||||||
|
:to="item.postNumber"
|
||||||
|
/>
|
||||||
|
</n-button >
|
||||||
|
<n-tag :bordered="false" type="warning" size="small">浏览量:<n-number-animation
|
||||||
|
show-separator
|
||||||
|
:from="0"
|
||||||
|
:to="item.clickNumber"
|
||||||
|
/>
|
||||||
|
</n-tag>
|
||||||
|
</n-flex>
|
||||||
|
</template>
|
||||||
|
</n-thing>
|
||||||
|
</n-list-item>
|
||||||
|
</n-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
115
frontend/src/components/IndustryResearchReportList.vue
Normal file
115
frontend/src/components/IndustryResearchReportList.vue
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onBeforeMount, ref} from 'vue'
|
||||||
|
import {GetStockList, IndustryResearchReport,EMDictCode} from "../../wailsjs/go/main/App";
|
||||||
|
import {ArrowDownOutline, CaretDown, CaretUp, PulseOutline, Refresh, RefreshCircleSharp,} from "@vicons/ionicons5";
|
||||||
|
|
||||||
|
import {useMessage} from "naive-ui";
|
||||||
|
import {BrowserOpenURL} from "../../wailsjs/runtime";
|
||||||
|
|
||||||
|
const message=useMessage()
|
||||||
|
const list = ref([])
|
||||||
|
|
||||||
|
const options = ref([])
|
||||||
|
|
||||||
|
function getIndustryResearchReport(value) {
|
||||||
|
message.loading("正在刷新数据...")
|
||||||
|
IndustryResearchReport(value).then(result => {
|
||||||
|
console.log(result)
|
||||||
|
list.value = result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(()=>{
|
||||||
|
getIndustryResearchReport('');
|
||||||
|
})
|
||||||
|
|
||||||
|
function ratingChangeName(ratingChange){
|
||||||
|
if(ratingChange===0){
|
||||||
|
return '调高'
|
||||||
|
}else if(ratingChange===1){
|
||||||
|
return '调低'
|
||||||
|
}else if(ratingChange===2){
|
||||||
|
return '首次'
|
||||||
|
}else if(ratingChange===3){
|
||||||
|
return '维持'
|
||||||
|
}else if (ratingChange===4){
|
||||||
|
return '无变化'
|
||||||
|
}else{
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function openWin(code) {
|
||||||
|
BrowserOpenURL("https://pdf.dfcfw.com/pdf/H3_"+code+"_1.pdf?1749744888000.pdf")
|
||||||
|
}
|
||||||
|
|
||||||
|
function EMDictCodeList(keyVal){
|
||||||
|
if (keyVal){
|
||||||
|
EMDictCode('016').then(result => {
|
||||||
|
console.log(result)
|
||||||
|
options.value=result.filter((value,index,array) => value.bkName.includes(keyVal)||value.firstLetter.includes(keyVal)||value.bkCode.includes(keyVal)).map(item => {
|
||||||
|
return {
|
||||||
|
label: item.bkName+" - "+item.bkCode,
|
||||||
|
value: item.bkCode
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
getIndustryResearchReport('')
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
function handleSearch(value) {
|
||||||
|
getIndustryResearchReport(value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-card>
|
||||||
|
<n-auto-complete :options="options" placeholder="请输入行业名称关键词搜索" clearable filterable :on-select="handleSearch" :on-update:value="EMDictCodeList" />
|
||||||
|
</n-card>
|
||||||
|
<n-table striped size="small">
|
||||||
|
<n-thead>
|
||||||
|
<n-tr>
|
||||||
|
<!-- <n-th>代码</n-th>-->
|
||||||
|
<!-- <n-th>名称</n-th>-->
|
||||||
|
<n-th>行业</n-th>
|
||||||
|
<n-th>标题</n-th>
|
||||||
|
<n-th>东财评级</n-th>
|
||||||
|
<n-th>评级变动</n-th>
|
||||||
|
<n-th>机构评级</n-th>
|
||||||
|
<n-th>分析师</n-th>
|
||||||
|
<n-th>机构</n-th>
|
||||||
|
<n-th> <n-flex justify="space-between">日期<n-icon @click="getIndustryResearchReport" color="#409EFF" :size="20" :component="RefreshCircleSharp"/></n-flex></n-th>
|
||||||
|
</n-tr>
|
||||||
|
</n-thead>
|
||||||
|
<n-tbody>
|
||||||
|
<n-tr v-for="item in list" :key="item.infoCode">
|
||||||
|
<!-- <n-td>{{item.stockCode}}</n-td>-->
|
||||||
|
<!-- <n-td :title="item.stockCode">-->
|
||||||
|
<!-- <n-popover trigger="hover" placement="right">-->
|
||||||
|
<!-- <template #trigger>-->
|
||||||
|
<!-- <n-tag type="info" :bordered="false">{{item.stockName}}</n-tag>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- <k-line-chart style="width: 800px" :code="getmMarketCode(item.market,item.stockCode)" :chart-height="500" :name="item.stockName" :k-days="20" :dark-theme="true"></k-line-chart>-->
|
||||||
|
<!-- </n-popover>-->
|
||||||
|
<!-- </n-td>-->
|
||||||
|
<n-td><n-tag type="info" :bordered="false">{{item.industryName}}</n-tag></n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-a type="info" @click="openWin(item.infoCode)"><n-text type="success">{{item.title}}</n-text></n-a>
|
||||||
|
</n-td>
|
||||||
|
<n-td><n-text :type="item.emRatingName==='增持'?'error':'info'">
|
||||||
|
{{item.emRatingName}}
|
||||||
|
</n-text></n-td>
|
||||||
|
<n-td><n-text :type="item.ratingChange===0?'error':'info'">{{ratingChangeName(item.ratingChange)}}</n-text></n-td>
|
||||||
|
<n-td>{{item.sRatingName }}</n-td>
|
||||||
|
<n-td><n-ellipsis style="max-width: 120px">{{item.researcher}}</n-ellipsis></n-td>
|
||||||
|
<n-td>{{item.orgSName}}</n-td>
|
||||||
|
<n-td>{{item.publishDate.substring(0,10)}}</n-td>
|
||||||
|
</n-tr>
|
||||||
|
</n-tbody>
|
||||||
|
</n-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
108
frontend/src/components/InvestCalendarTimeLine.vue
Normal file
108
frontend/src/components/InvestCalendarTimeLine.vue
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {nextTick, onBeforeMount, onMounted, onUnmounted, ref} from 'vue'
|
||||||
|
import {InvestCalendarTimeLine} from "../../wailsjs/go/main/App";
|
||||||
|
import { addMonths, format ,parse} from 'date-fns';
|
||||||
|
import { zhCN } from 'date-fns/locale';
|
||||||
|
|
||||||
|
import {useMessage} from 'naive-ui'
|
||||||
|
import {Star48Filled} from "@vicons/fluent";
|
||||||
|
const today = new Date();
|
||||||
|
const year = today.getFullYear();
|
||||||
|
const month = String(today.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要+1
|
||||||
|
const day = String(today.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
|
// 常见格式:YYYY-MM-DD
|
||||||
|
const formattedDate = `${year}-${month}-${day}`;
|
||||||
|
const formattedYM = `${year}-${month}`;
|
||||||
|
const list = ref([])
|
||||||
|
const message=useMessage()
|
||||||
|
|
||||||
|
function goBackToday() {
|
||||||
|
setTimeout(() => {
|
||||||
|
nextTick(
|
||||||
|
() => {
|
||||||
|
const elementById = document.getElementById(formattedDate);
|
||||||
|
if (elementById) {
|
||||||
|
elementById.scrollIntoView({
|
||||||
|
behavior: 'auto',
|
||||||
|
block: 'start'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
InvestCalendarTimeLine(formattedYM).then(res => {
|
||||||
|
list.value = res
|
||||||
|
goBackToday();
|
||||||
|
})
|
||||||
|
})
|
||||||
|
onMounted(()=>{
|
||||||
|
|
||||||
|
})
|
||||||
|
function loadMore(){
|
||||||
|
if (list.value.length>0){
|
||||||
|
let day=parse(list.value[list.value.length-1].date, 'yyyy-MM-dd', new Date())
|
||||||
|
let nextMonth=addMonths(day,1)
|
||||||
|
let ym = format(nextMonth, 'yyyy-MM');
|
||||||
|
console.log(ym)
|
||||||
|
InvestCalendarTimeLine(ym).then(res => {
|
||||||
|
if (res.length==0){
|
||||||
|
message.warning("没有更多数据了")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list.value.push( ...res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getweekday(date){
|
||||||
|
let day=parse(date, 'yyyy-MM-dd', new Date())
|
||||||
|
return format(day, 'EEEE', {locale: zhCN})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- <n-timeline size="large" style="text-align: left">-->
|
||||||
|
<!-- <n-timeline-item v-for="item in list" :key="item.date" :title="item.date" type="info" >-->
|
||||||
|
<!-- <n-list>-->
|
||||||
|
<!-- <n-list-item v-for="l in item.list" :key="l.article_id ">-->
|
||||||
|
<!-- <n-text>{{l.title}}</n-text>-->
|
||||||
|
<!-- </n-list-item>-->
|
||||||
|
<!-- </n-list>-->
|
||||||
|
<!-- </n-timeline-item>-->
|
||||||
|
<!-- </n-timeline>-->
|
||||||
|
|
||||||
|
<n-list bordered style="max-height: calc(100vh - 230px);text-align: left;">
|
||||||
|
<n-scrollbar style="max-height: calc(100vh - 230px);" >
|
||||||
|
<n-list-item v-for="(item, index) in list" :id="item.date" :key="item.date">
|
||||||
|
<n-thing :title="item.date+' '+getweekday(item.date)">
|
||||||
|
<n-list :bordered="false" hoverable>
|
||||||
|
<n-list-item v-for="(l,i ) in item.list" :key="l.article_id ">
|
||||||
|
<n-flex justify="space-between">
|
||||||
|
<n-text :type="item.date===formattedDate?'warning':'info'">{{i+1}}# {{l.title}}</n-text>
|
||||||
|
<n-rate v-if="l.like_count>0" readonly :default-value="l.like_count" :count="l.like_count" >
|
||||||
|
<n-icon :component="Star48Filled"/>
|
||||||
|
</n-rate>
|
||||||
|
</n-flex>
|
||||||
|
</n-list-item>
|
||||||
|
</n-list>
|
||||||
|
</n-thing>
|
||||||
|
</n-list-item>
|
||||||
|
<n-list-item v-if="list.length==0">
|
||||||
|
<n-text type="info">没有数据</n-text>
|
||||||
|
</n-list-item>
|
||||||
|
<n-list-item v-else style="text-align: center;">
|
||||||
|
<n-button-group>
|
||||||
|
<n-button strong secondary type="info" @click="loadMore">加载更多</n-button>
|
||||||
|
<n-button strong secondary type="warning" @click="goBackToday">回到今天</n-button>
|
||||||
|
</n-button-group>
|
||||||
|
</n-list-item>
|
||||||
|
</n-scrollbar>
|
||||||
|
</n-list>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -60,7 +60,7 @@ function handleKLine(code,name){
|
|||||||
////console.log("values",values)
|
////console.log("values",values)
|
||||||
let option = {
|
let option = {
|
||||||
title: {
|
title: {
|
||||||
text: name,
|
text: name+" "+code,
|
||||||
left: '20px',
|
left: '20px',
|
||||||
textStyle: {
|
textStyle: {
|
||||||
color: darkTheme?'#ccc':'#456'
|
color: darkTheme?'#ccc':'#456'
|
||||||
@ -356,7 +356,7 @@ function handleKLine(code,name){
|
|||||||
chart.setOption(option);
|
chart.setOption(option);
|
||||||
|
|
||||||
chart.on('click',{seriesName:'日K'}, function(params) {
|
chart.on('click',{seriesName:'日K'}, function(params) {
|
||||||
console.log("click:",params);
|
//console.log("click:",params);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
229
frontend/src/components/LongTigerRankList.vue
Normal file
229
frontend/src/components/LongTigerRankList.vue
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {onBeforeMount, ref} from 'vue'
|
||||||
|
import {LongTigerRank} from "../../wailsjs/go/main/App";
|
||||||
|
import {BrowserOpenURL} from "../../wailsjs/runtime";
|
||||||
|
import {ArrowDownOutline} from "@vicons/ionicons5";
|
||||||
|
import _ from "lodash";
|
||||||
|
import KLineChart from "./KLineChart.vue";
|
||||||
|
import MoneyTrend from "./moneyTrend.vue";
|
||||||
|
import {NButton, NText, useMessage} from "naive-ui";
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const lhbList= ref([])
|
||||||
|
const EXPLANATIONs = ref([])
|
||||||
|
|
||||||
|
const today = new Date();
|
||||||
|
const year = today.getFullYear();
|
||||||
|
const month = String(today.getMonth() + 1).padStart(2, '0'); // 月份从0开始,需要+1
|
||||||
|
const day = String(today.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
|
// 常见格式:YYYY-MM-DD
|
||||||
|
const formattedDate = `${year}-${month}-${day}`;
|
||||||
|
|
||||||
|
const SearchForm= ref({
|
||||||
|
dateValue: formattedDate,
|
||||||
|
EXPLANATION:null,
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
longTiger(formattedDate);
|
||||||
|
})
|
||||||
|
function longTiger_old(date) {
|
||||||
|
if(date) {
|
||||||
|
SearchForm.value.dateValue = date
|
||||||
|
}
|
||||||
|
let loading1=message.loading("正在获取龙虎榜数据...",{
|
||||||
|
duration: 0,
|
||||||
|
})
|
||||||
|
LongTigerRank(date).then(res => {
|
||||||
|
lhbList.value = res
|
||||||
|
loading1.destroy()
|
||||||
|
if (res.length === 0) {
|
||||||
|
message.info("暂无数据,请切换日期")
|
||||||
|
}
|
||||||
|
EXPLANATIONs.value=_.uniqBy(_.map(lhbList.value,function (item){
|
||||||
|
return {
|
||||||
|
label: item['EXPLANATION'],
|
||||||
|
value: item['EXPLANATION'],
|
||||||
|
}
|
||||||
|
}),'label');
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function longTiger(date) {
|
||||||
|
if (date) {
|
||||||
|
SearchForm.value.dateValue = date;
|
||||||
|
}
|
||||||
|
|
||||||
|
let loading1 = message.loading("正在获取龙虎榜数据...", {
|
||||||
|
duration: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchDate = (currentDate, retryCount = 0) => {
|
||||||
|
if (retryCount > 7) { // 防止无限循环,最多尝试7次
|
||||||
|
lhbList.value = [];
|
||||||
|
EXPLANATIONs.value = [];
|
||||||
|
loading1.destroy();
|
||||||
|
message.info("暂无历史数据");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LongTigerRank(currentDate).then(res => {
|
||||||
|
if (res.length === 0) {
|
||||||
|
const previousDate = new Date(currentDate);
|
||||||
|
previousDate.setDate(previousDate.getDate() - 1);
|
||||||
|
|
||||||
|
const year = previousDate.getFullYear();
|
||||||
|
const month = String(previousDate.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(previousDate.getDate()).padStart(2, '0');
|
||||||
|
const prevFormattedDate = `${year}-${month}-${day}`;
|
||||||
|
|
||||||
|
message.info(`当前日期 ${currentDate} 暂无数据,尝试查询前一日:${prevFormattedDate}`);
|
||||||
|
|
||||||
|
SearchForm.value.dateValue = prevFormattedDate;
|
||||||
|
fetchDate(prevFormattedDate, retryCount + 1); // 递归调用
|
||||||
|
} else {
|
||||||
|
lhbList.value = res;
|
||||||
|
loading1.destroy();
|
||||||
|
EXPLANATIONs.value = _.uniqBy(_.map(lhbList.value, function (item) {
|
||||||
|
return {
|
||||||
|
label: item['EXPLANATION'],
|
||||||
|
value: item['EXPLANATION'],
|
||||||
|
};
|
||||||
|
}), 'label');
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
loading1.destroy();
|
||||||
|
message.error("获取数据失败,请重试");
|
||||||
|
console.error(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchDate(date || formattedDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEXPLANATION(value, option){
|
||||||
|
SearchForm.value.EXPLANATION = value
|
||||||
|
if(value){
|
||||||
|
LongTigerRank(SearchForm.value.dateValue).then(res => {
|
||||||
|
lhbList.value=_.filter(res, function(o) { return o['EXPLANATION']===value; });
|
||||||
|
if (res.length === 0) {
|
||||||
|
message.info("暂无数据,请切换日期")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
longTiger(SearchForm.value.dateValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-form :model="SearchForm" >
|
||||||
|
<n-grid :cols="24" :x-gap="24">
|
||||||
|
<n-form-item-gi :span="4" label="日期" path="dateValue" label-placement="left">
|
||||||
|
<n-date-picker v-model:formatted-value="SearchForm.dateValue"
|
||||||
|
value-format="yyyy-MM-dd" type="date" :on-update:value="(v,v2)=>longTiger(v2)"/>
|
||||||
|
|
||||||
|
</n-form-item-gi>
|
||||||
|
<n-form-item-gi :span="8" label="上榜原因" path="EXPLANATION" label-placement="left">
|
||||||
|
<n-select clearable placeholder="上榜原因过滤" v-model:value="SearchForm.EXPLANATION" :options="EXPLANATIONs" :on-update:value="handleEXPLANATION"/>
|
||||||
|
</n-form-item-gi>
|
||||||
|
<n-form-item-gi :span="10" label="" label-placement="left">
|
||||||
|
<n-text type="error">*当天的龙虎榜数据通常在收盘结束后一小时左右更新</n-text>
|
||||||
|
</n-form-item-gi>
|
||||||
|
</n-grid>
|
||||||
|
</n-form>
|
||||||
|
<n-table :single-line="false" striped>
|
||||||
|
<n-thead>
|
||||||
|
<n-tr>
|
||||||
|
<n-th>代码</n-th>
|
||||||
|
<!-- <n-th width="90px">日期</n-th>-->
|
||||||
|
<n-th width="60px">名称</n-th>
|
||||||
|
<n-th>收盘价</n-th>
|
||||||
|
<n-th width="60px">涨跌幅</n-th>
|
||||||
|
<n-th>龙虎榜净买额(万)</n-th>
|
||||||
|
<n-th>龙虎榜买入额(万)</n-th>
|
||||||
|
<n-th>龙虎榜卖出额(万)</n-th>
|
||||||
|
<n-th>龙虎榜成交额(万)</n-th>
|
||||||
|
<!-- <n-th>市场总成交额(万)</n-th>-->
|
||||||
|
<!-- <n-th>净买额占总成交比</n-th>-->
|
||||||
|
<!-- <n-th>成交额占总成交比</n-th>-->
|
||||||
|
<n-th width="60px" data-field="TURNOVERRATE">换手率<n-icon :component="ArrowDownOutline" /></n-th>
|
||||||
|
<n-th>流通市值(亿)</n-th>
|
||||||
|
<n-th>上榜原因</n-th>
|
||||||
|
<!-- <n-th>解读</n-th>-->
|
||||||
|
</n-tr>
|
||||||
|
</n-thead>
|
||||||
|
<n-tbody>
|
||||||
|
<n-tr v-for="(item, index) in lhbList" :key="index">
|
||||||
|
<n-td>
|
||||||
|
<n-tag :bordered=false type="info">{{ item.SECUCODE.split('.')[1].toLowerCase()+item.SECUCODE.split('.')[0] }}</n-tag>
|
||||||
|
</n-td>
|
||||||
|
<!-- <n-td>
|
||||||
|
{{item.TRADE_DATE.substring(0,10)}}
|
||||||
|
</n-td>-->
|
||||||
|
<n-td>
|
||||||
|
<!-- <n-text :type="item.CHANGE_RATE>0?'error':'success'">{{ item.SECURITY_NAME_ABBR }}</n-text>-->
|
||||||
|
<n-popover trigger="hover" placement="right">
|
||||||
|
<template #trigger>
|
||||||
|
<n-button tag="a" text :type="item.CHANGE_RATE>0?'error':'success'" :bordered=false >{{ item.SECURITY_NAME_ABBR }}</n-button>
|
||||||
|
</template>
|
||||||
|
<k-line-chart style="width: 800px" :code="item.SECUCODE.split('.')[1].toLowerCase()+item.SECUCODE.split('.')[0]" :chart-height="500" :name="item.SECURITY_NAME_ABBR" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||||
|
</n-popover>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.CHANGE_RATE>0?'error':'success'">{{ item.CLOSE_PRICE }}</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.CHANGE_RATE>0?'error':'success'">{{ (item.CHANGE_RATE).toFixed(2) }}%</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<!-- <n-text :type="item.BILLBOARD_NET_AMT>0?'error':'success'">{{ (item.BILLBOARD_NET_AMT/10000).toFixed(2) }}</n-text>-->
|
||||||
|
|
||||||
|
|
||||||
|
<n-popover trigger="hover" placement="right">
|
||||||
|
<template #trigger>
|
||||||
|
<n-button tag="a" text :type="item.BILLBOARD_NET_AMT>0?'error':'success'" :bordered=false >{{ (item.BILLBOARD_NET_AMT/10000).toFixed(2) }}</n-button>
|
||||||
|
</template>
|
||||||
|
<money-trend :code="item.SECUCODE.split('.')[1].toLowerCase()+item.SECUCODE.split('.')[0]" :name="item.SECURITY_NAME_ABBR" :days="360" :dark-theme="true" :chart-height="500" style="width: 800px"></money-trend>
|
||||||
|
</n-popover>
|
||||||
|
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="'error'">{{ (item.BILLBOARD_BUY_AMT/10000).toFixed(2) }}</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="'success'">{{ (item.BILLBOARD_SELL_AMT/10000).toFixed(2) }}</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="'info'">{{ (item.BILLBOARD_DEAL_AMT/10000).toFixed(2) }}</n-text>
|
||||||
|
</n-td>
|
||||||
|
<!-- <n-td>-->
|
||||||
|
<!-- <n-text :type="'info'">{{ (item.ACCUM_AMOUNT/10000).toFixed(2) }}</n-text>-->
|
||||||
|
<!-- </n-td>-->
|
||||||
|
<!-- <n-td>-->
|
||||||
|
<!-- <n-text :type="item.DEAL_NET_RATIO>0?'error':'success'">{{ (item.DEAL_NET_RATIO).toFixed(2) }}%</n-text>-->
|
||||||
|
<!-- </n-td>-->
|
||||||
|
<!-- <n-td>-->
|
||||||
|
<!-- <n-text :type="'info'">{{ (item.DEAL_AMOUNT_RATIO).toFixed(2) }}%</n-text>-->
|
||||||
|
<!-- </n-td>-->
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="'info'">{{ (item.TURNOVERRATE).toFixed(2) }}%</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="'info'">{{ (item.FREE_MARKET_CAP/100000000).toFixed(2) }}</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="'info'">{{ item.EXPLANATION }}</n-text>
|
||||||
|
</n-td>
|
||||||
|
<!-- <n-td>
|
||||||
|
<n-text :type="item.CHANGE_RATE>0?'error':'success'">{{ item.EXPLAIN }}</n-text>
|
||||||
|
</n-td>-->
|
||||||
|
</n-tr>
|
||||||
|
</n-tbody>
|
||||||
|
</n-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
126
frontend/src/components/SelectStock.vue
Normal file
126
frontend/src/components/SelectStock.vue
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {h, onBeforeMount, onMounted, onUnmounted, ref} from 'vue'
|
||||||
|
import {SearchStock} from "../../wailsjs/go/main/App";
|
||||||
|
import {useMessage, NText, NTag} from 'naive-ui'
|
||||||
|
const message = useMessage()
|
||||||
|
const search = ref('科技股;换手率连续3日大于2')
|
||||||
|
const columns = ref([])
|
||||||
|
const dataList = ref([])
|
||||||
|
|
||||||
|
function Search() {
|
||||||
|
const loading = message.loading("正在获取选股数据...", {duration: 0});
|
||||||
|
SearchStock(search.value).then(res => {
|
||||||
|
loading.destroy()
|
||||||
|
//console.log(res)
|
||||||
|
if(res.code==100){
|
||||||
|
message.success(res.msg)
|
||||||
|
columns.value=res.data.result.columns.filter(item=>!item.hiddenNeed&&(item.title!="市场码"&&item.title!="市场简称")).map(item=>{
|
||||||
|
|
||||||
|
if(item.children){
|
||||||
|
return {
|
||||||
|
title:item.title+(item.unit?'['+item.unit+']':''),
|
||||||
|
key:item.key,
|
||||||
|
resizable: true,
|
||||||
|
minWidth:200,
|
||||||
|
ellipsis: {
|
||||||
|
tooltip: true
|
||||||
|
},
|
||||||
|
children:item.children.filter(item=>!item.hiddenNeed).map(item=>{
|
||||||
|
return {
|
||||||
|
title:item.dateMsg,
|
||||||
|
key:item.key,
|
||||||
|
minWidth:100,
|
||||||
|
resizable: true,
|
||||||
|
ellipsis: {
|
||||||
|
tooltip: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
return {
|
||||||
|
title:item.title+(item.unit?'['+item.unit+']':''),
|
||||||
|
key:item.key,
|
||||||
|
resizable: true,
|
||||||
|
minWidth:100,
|
||||||
|
ellipsis: {
|
||||||
|
tooltip: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dataList.value=res.data.result.dataList
|
||||||
|
}else {
|
||||||
|
message.error(res.msg)
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
message.error(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function isNumeric(value) {
|
||||||
|
return !isNaN(parseFloat(value)) && isFinite(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
Search()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-flex>
|
||||||
|
<n-input-group>
|
||||||
|
<n-input v-model:value="search" placeholder="请输入选股指标或者要求" />
|
||||||
|
<n-button type="success" @click="Search">搜索A股</n-button>
|
||||||
|
</n-input-group>
|
||||||
|
</n-flex>
|
||||||
|
<!-- <n-table striped size="small">-->
|
||||||
|
<!-- <n-thead>-->
|
||||||
|
<!-- <n-tr>-->
|
||||||
|
<!-- <n-th v-for="item in columns">{{item.title}}</n-th>-->
|
||||||
|
<!-- </n-tr>-->
|
||||||
|
<!-- </n-thead>-->
|
||||||
|
<!-- <n-tbody>-->
|
||||||
|
<!-- <n-tr v-for="(item,index) in dataList">-->
|
||||||
|
<!-- <n-td v-for="d in columns">{{item[d.key]}}</n-td>-->
|
||||||
|
<!-- </n-tr>-->
|
||||||
|
<!-- </n-tbody>-->
|
||||||
|
<!-- </n-table>-->
|
||||||
|
<n-data-table
|
||||||
|
:max-height="'calc(100vh - 285px)'"
|
||||||
|
size="small"
|
||||||
|
:columns="columns"
|
||||||
|
:data="dataList"
|
||||||
|
:pagination="false"
|
||||||
|
:scroll-x="1800"
|
||||||
|
:render-cell="(value, rowData, column) => {
|
||||||
|
|
||||||
|
if(column.key=='SECURITY_CODE'||column.key=='SERIAL'){
|
||||||
|
return h(NText, { type: 'info',border: false }, { default: () => `${value}` })
|
||||||
|
}
|
||||||
|
if (isNumeric(value)) {
|
||||||
|
let type='info';
|
||||||
|
if (Number(value)<0){
|
||||||
|
type='success';
|
||||||
|
}
|
||||||
|
if(Number(value)>=0&&Number(value)<=5){
|
||||||
|
type='warning';
|
||||||
|
}
|
||||||
|
if (Number(value)>5){
|
||||||
|
type='error';
|
||||||
|
}
|
||||||
|
return h(NText, { type: type }, { default: () => `${value}` })
|
||||||
|
}else{
|
||||||
|
if(column.key=='SECURITY_SHORT_NAME'){
|
||||||
|
return h(NTag, { type: 'info',bordered: false }, { default: () => `${value}` })
|
||||||
|
}else{
|
||||||
|
return h(NText, { type: 'info' }, { default: () => `${value}` })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
148
frontend/src/components/StockNoticeList.vue
Normal file
148
frontend/src/components/StockNoticeList.vue
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {onBeforeMount, ref} from 'vue'
|
||||||
|
import {GetStockList, StockNotice} from "../../wailsjs/go/main/App";
|
||||||
|
import {BrowserOpenURL} from "../../wailsjs/runtime";
|
||||||
|
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)
|
||||||
|
list.value = result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount (()=>{
|
||||||
|
//message.info("正在获取数据"+stockCode)
|
||||||
|
getNotice(stockCode);
|
||||||
|
})
|
||||||
|
|
||||||
|
function findStockList(query){
|
||||||
|
if (query){
|
||||||
|
GetStockList(query).then(result => {
|
||||||
|
options.value=result.map(item => {
|
||||||
|
return {
|
||||||
|
label: item.name+" - "+item.ts_code,
|
||||||
|
value: item.ts_code
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
getNotice("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleSearch(value) {
|
||||||
|
getNotice(value)
|
||||||
|
}
|
||||||
|
function openWin(code) {
|
||||||
|
BrowserOpenURL("https://pdf.dfcfw.com/pdf/H2_"+code+"_1.pdf?1750092081000.pdf")
|
||||||
|
}
|
||||||
|
function getTypeColor(name){
|
||||||
|
if(name.includes("质押")||name.includes("冻结")||name.includes("解冻")||name.includes("解押")||name.includes("解禁")){
|
||||||
|
return "error"
|
||||||
|
}
|
||||||
|
if(name.includes("异常")||name.includes("减持")||name.includes("增发")||name.includes("重大")){
|
||||||
|
return "error"
|
||||||
|
}
|
||||||
|
if(name.includes("季度报告")||name.includes("年度报告")||name.includes("澄清公告")||name.includes("风险")){
|
||||||
|
return "error"
|
||||||
|
}
|
||||||
|
if(name.includes("终止")||name.includes("复牌")||name.includes("停牌")||name.includes("退市")){
|
||||||
|
return "error"
|
||||||
|
}
|
||||||
|
if(name.includes("破产")||name.includes("清算")){
|
||||||
|
return "error"
|
||||||
|
}
|
||||||
|
if(name.includes("回购")||name.includes("重组")||name.includes("诉讼")||name.includes("仲裁")||name.includes("转让")||name.includes("收购")){
|
||||||
|
return "warning"
|
||||||
|
}
|
||||||
|
if(name.includes("调研")||name.includes("募集")){
|
||||||
|
return "warning"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "info"
|
||||||
|
|
||||||
|
}
|
||||||
|
function getmMarketCode(market,code) {
|
||||||
|
if(market==="0"){
|
||||||
|
return "sz"+code
|
||||||
|
}else if(market==="1"){
|
||||||
|
return "sh"+code
|
||||||
|
}else if(market==="2"){
|
||||||
|
return "bj"+code
|
||||||
|
}else if(market==="3"){
|
||||||
|
return "hk"+code
|
||||||
|
}else{
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-card>
|
||||||
|
<n-auto-complete :options="options" placeholder="请输入A股名称或者代码" clearable filterable :on-select="handleSearch" :on-update:value="findStockList" />
|
||||||
|
</n-card>
|
||||||
|
<n-table striped size="small">
|
||||||
|
<n-thead>
|
||||||
|
<n-tr>
|
||||||
|
<n-th>股票代码</n-th>
|
||||||
|
<n-th>股票名称</n-th>
|
||||||
|
<n-th>公告标题</n-th>
|
||||||
|
<n-th>公告类型</n-th>
|
||||||
|
<n-th>公告日期</n-th>
|
||||||
|
<n-th><n-flex>数据更新时间<n-icon @click="getNotice('')" color="#409EFF" :size="20" :component="RefreshCircleSharp"/></n-flex></n-th>
|
||||||
|
</n-tr>
|
||||||
|
</n-thead>
|
||||||
|
<n-tbody>
|
||||||
|
<n-tr v-for="item in list" :key="item.art_code">
|
||||||
|
<n-td>
|
||||||
|
<n-popover trigger="hover" placement="right">
|
||||||
|
<template #trigger>
|
||||||
|
<n-tag type="info" :bordered="false">{{item.codes[0].stock_code }}</n-tag>
|
||||||
|
</template>
|
||||||
|
<money-trend style="width: 800px" :code="getmMarketCode(item.codes[0].market_code,item.codes[0].stock_code)" :name="item.codes[0].short_name" :days="360" :dark-theme="true" :chart-height="500"></money-trend>
|
||||||
|
</n-popover>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-popover trigger="hover" placement="right">
|
||||||
|
<template #trigger>
|
||||||
|
<n-tag type="info" :bordered="false">{{item.codes[0].short_name }}</n-tag>
|
||||||
|
</template>
|
||||||
|
<k-line-chart style="width: 800px" :code="getmMarketCode(item.codes[0].market_code,item.codes[0].stock_code)" :chart-height="500" :name="item.codes[0].short_name" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||||
|
</n-popover>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-a type="info" @click="openWin(item.art_code)"><n-text :type="getTypeColor(item.columns[0].column_name)"> {{item.title}}</n-text></n-a>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="getTypeColor(item.columns[0].column_name)">{{item.columns[0].column_name }}</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-tag type="info">{{item.notice_date.substring(0,10) }}</n-tag>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-tag type="info">{{item.display_time.substring(0,19)}}</n-tag>
|
||||||
|
</n-td>
|
||||||
|
</n-tr>
|
||||||
|
</n-tbody>
|
||||||
|
</n-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
136
frontend/src/components/StockResearchReportList.vue
Normal file
136
frontend/src/components/StockResearchReportList.vue
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<script setup>
|
||||||
|
import {onBeforeMount, ref} from 'vue'
|
||||||
|
import {GetStockList, StockResearchReport} from "../../wailsjs/go/main/App";
|
||||||
|
import {ArrowDownOutline, CaretDown, CaretUp, PulseOutline, Refresh, RefreshCircleSharp,} from "@vicons/ionicons5";
|
||||||
|
|
||||||
|
import KLineChart from "./KLineChart.vue";
|
||||||
|
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([])
|
||||||
|
|
||||||
|
const options = ref([])
|
||||||
|
|
||||||
|
function getStockResearchReport(value) {
|
||||||
|
StockResearchReport(value).then(result => {
|
||||||
|
//console.log(result)
|
||||||
|
list.value = result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(()=>{
|
||||||
|
getStockResearchReport(stockCode);
|
||||||
|
})
|
||||||
|
|
||||||
|
function ratingChangeName(ratingChange){
|
||||||
|
if(ratingChange===0){
|
||||||
|
return '调高'
|
||||||
|
}else if(ratingChange===1){
|
||||||
|
return '调低'
|
||||||
|
}else if(ratingChange===2){
|
||||||
|
return '首次'
|
||||||
|
}else if(ratingChange===3){
|
||||||
|
return '维持'
|
||||||
|
}else if (ratingChange===4){
|
||||||
|
return '无变化'
|
||||||
|
}else{
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getmMarketCode(market,code) {
|
||||||
|
if(market==="SHENZHEN"){
|
||||||
|
return "sz"+code
|
||||||
|
}else if(market==="SHANGHAI"){
|
||||||
|
return "sh"+code
|
||||||
|
}else if(market==="BEIJING"){
|
||||||
|
return "bj"+code
|
||||||
|
}else if(market==="HONGKONG"){
|
||||||
|
return "hk"+code
|
||||||
|
}else{
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function openWin(code) {
|
||||||
|
BrowserOpenURL("https://pdf.dfcfw.com/pdf/H3_"+code+"_1.pdf?1749744888000.pdf")
|
||||||
|
}
|
||||||
|
|
||||||
|
function findStockList(query){
|
||||||
|
if (query){
|
||||||
|
GetStockList(query).then(result => {
|
||||||
|
options.value=result.map(item => {
|
||||||
|
return {
|
||||||
|
label: item.name+" - "+item.ts_code,
|
||||||
|
value: item.ts_code
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
getStockResearchReport('')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handleSearch(value) {
|
||||||
|
getStockResearchReport(value)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-card>
|
||||||
|
<n-auto-complete :options="options" placeholder="请输入A股名称或者代码" clearable filterable :on-select="handleSearch" :on-update:value="findStockList" />
|
||||||
|
</n-card>
|
||||||
|
<n-table striped size="small">
|
||||||
|
<n-thead>
|
||||||
|
<n-tr>
|
||||||
|
<!-- <n-th>代码</n-th>-->
|
||||||
|
<n-th>名称</n-th>
|
||||||
|
<n-th>行业</n-th>
|
||||||
|
<n-th>标题</n-th>
|
||||||
|
<n-th>东财评级</n-th>
|
||||||
|
<n-th>评级变动</n-th>
|
||||||
|
<n-th>机构评级</n-th>
|
||||||
|
<n-th>分析师</n-th>
|
||||||
|
<n-th>机构</n-th>
|
||||||
|
<n-th> <n-flex justify="space-between">日期<n-icon @click="getStockResearchReport" color="#409EFF" :size="20" :component="RefreshCircleSharp"/></n-flex></n-th>
|
||||||
|
</n-tr>
|
||||||
|
</n-thead>
|
||||||
|
<n-tbody>
|
||||||
|
<n-tr v-for="item in list" :key="item.infoCode">
|
||||||
|
<!-- <n-td>{{item.stockCode}}</n-td>-->
|
||||||
|
<n-td :title="item.stockCode">
|
||||||
|
<n-popover trigger="hover" placement="right">
|
||||||
|
<template #trigger>
|
||||||
|
<n-tag type="info" :bordered="false">{{item.stockName}}</n-tag>
|
||||||
|
</template>
|
||||||
|
<k-line-chart style="width: 800px" :code="getmMarketCode(item.market,item.stockCode)" :chart-height="500" :name="item.stockName" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||||
|
</n-popover>
|
||||||
|
</n-td>
|
||||||
|
<n-td><n-tag type="info" :bordered="false">{{item.indvInduName}}</n-tag></n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-a type="info" @click="openWin(item.infoCode)">{{item.title}}</n-a>
|
||||||
|
</n-td>
|
||||||
|
<n-td><n-text :type="item.emRatingName==='增持'?'error':'info'">
|
||||||
|
{{item.emRatingName}}
|
||||||
|
</n-text></n-td>
|
||||||
|
<n-td><n-text :type="item.ratingChange===0?'error':'info'">{{ratingChangeName(item.ratingChange)}}</n-text></n-td>
|
||||||
|
<n-td>{{item.sRatingName}}</n-td>
|
||||||
|
<n-td>{{item.researcher}}</n-td>
|
||||||
|
<n-td>{{item.orgSName}}</n-td>
|
||||||
|
<n-td>{{item.publishDate.substring(0,10)}}</n-td>
|
||||||
|
</n-tr>
|
||||||
|
</n-tbody>
|
||||||
|
</n-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -44,8 +44,8 @@ EventsOn("updateVersion",async (msg) => {
|
|||||||
|
|
||||||
const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||||
|
|
||||||
console.log("GitHub UTC 时间:", utcDate);
|
//console.log("GitHub UTC 时间:", utcDate);
|
||||||
console.log("转换后的本地时间:", formattedDate);
|
//console.log("转换后的本地时间:", formattedDate);
|
||||||
notify.info({
|
notify.info({
|
||||||
avatar: () =>
|
avatar: () =>
|
||||||
h(NAvatar, {
|
h(NAvatar, {
|
||||||
@ -135,6 +135,7 @@ EventsOn("updateVersion",async (msg) => {
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
感谢以下开发者:
|
感谢以下开发者:
|
||||||
|
<a href="https://github.com/CodeNoobLH" target="_blank">浓睡不消残酒</a><n-divider vertical />
|
||||||
<a href="https://github.com/gnim2600" target="_blank">@gnim2600</a><n-divider vertical />
|
<a href="https://github.com/gnim2600" target="_blank">@gnim2600</a><n-divider vertical />
|
||||||
<a href="https://github.com/XXXiaohuayanGGG" target="_blank">@XXXiaohuayanGGG</a><n-divider vertical />
|
<a href="https://github.com/XXXiaohuayanGGG" target="_blank">@XXXiaohuayanGGG</a><n-divider vertical />
|
||||||
<a href="https://github.com/2lovecode" target="_blank">@2lovecode</a><n-divider vertical />
|
<a href="https://github.com/2lovecode" target="_blank">@2lovecode</a><n-divider vertical />
|
||||||
|
@ -47,7 +47,7 @@ onBeforeMount(()=>{
|
|||||||
})
|
})
|
||||||
GetFollowedFund().then(result => {
|
GetFollowedFund().then(result => {
|
||||||
followList.value = result
|
followList.value = result
|
||||||
console.log("followList",followList.value)
|
//console.log("followList",followList.value)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ onMounted(() => {
|
|||||||
//ws.value = new WebSocket('ws://localhost:16688/ws'); // 替换为你的 WebSocket 服务器地址
|
//ws.value = new WebSocket('ws://localhost:16688/ws'); // 替换为你的 WebSocket 服务器地址
|
||||||
|
|
||||||
ws.value.onopen = () => {
|
ws.value.onopen = () => {
|
||||||
console.log('WebSocket 连接已打开');
|
//console.log('WebSocket 连接已打开');
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.value.onmessage = (event) => {
|
ws.value.onmessage = (event) => {
|
||||||
@ -74,13 +74,13 @@ onMounted(() => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ws.value.onclose = () => {
|
ws.value.onclose = () => {
|
||||||
console.log('WebSocket 连接已关闭');
|
//console.log('WebSocket 连接已关闭');
|
||||||
};
|
};
|
||||||
|
|
||||||
ticker.value=setInterval(() => {
|
ticker.value=setInterval(() => {
|
||||||
GetFollowedFund().then(result => {
|
GetFollowedFund().then(result => {
|
||||||
followList.value = result
|
followList.value = result
|
||||||
console.log("followList",followList.value)
|
//console.log("followList",followList.value)
|
||||||
})
|
})
|
||||||
}, 1000*60)
|
}, 1000*60)
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ function AddFund(){
|
|||||||
message.success("关注成功")
|
message.success("关注成功")
|
||||||
GetFollowedFund().then(result => {
|
GetFollowedFund().then(result => {
|
||||||
followList.value = result
|
followList.value = result
|
||||||
console.log("followList",followList.value)
|
//console.log("followList",followList.value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -114,7 +114,7 @@ function unFollow(code){
|
|||||||
message.success("取消关注成功")
|
message.success("取消关注成功")
|
||||||
GetFollowedFund().then(result => {
|
GetFollowedFund().then(result => {
|
||||||
followList.value = result
|
followList.value = result
|
||||||
console.log("followList",followList.value)
|
//console.log("followList",followList.value)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
94
frontend/src/components/industryMoneyRank.vue
Normal file
94
frontend/src/components/industryMoneyRank.vue
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<script setup>
|
||||||
|
|
||||||
|
import {CaretDown, CaretUp, RefreshCircleOutline} from "@vicons/ionicons5";
|
||||||
|
import {NText,useMessage} from "naive-ui";
|
||||||
|
import {onBeforeUnmount, onMounted, onUnmounted, ref} from "vue";
|
||||||
|
import {GetIndustryMoneyRankSina} from "../../wailsjs/go/main/App";
|
||||||
|
import KLineChart from "./KLineChart.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
headerTitle: {
|
||||||
|
type: String,
|
||||||
|
default: '行业资金排名(净流入)'
|
||||||
|
},
|
||||||
|
fenlei: {
|
||||||
|
type: String,
|
||||||
|
default: '0'
|
||||||
|
},
|
||||||
|
sort: {
|
||||||
|
type: String,
|
||||||
|
default: 'netamount'
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const message = useMessage()
|
||||||
|
const dataList= ref([])
|
||||||
|
const sort = ref(props.sort)
|
||||||
|
const fenlei= ref(props.fenlei)
|
||||||
|
|
||||||
|
const interval = ref(null)
|
||||||
|
onMounted(()=>{
|
||||||
|
sort.value=props.sort
|
||||||
|
fenlei.value=props.fenlei
|
||||||
|
GetRankData()
|
||||||
|
interval.value=setInterval(()=>{
|
||||||
|
GetRankData()
|
||||||
|
},1000*60)
|
||||||
|
})
|
||||||
|
onBeforeUnmount(()=>{
|
||||||
|
clearInterval(interval.value)
|
||||||
|
})
|
||||||
|
function GetRankData(){
|
||||||
|
message.loading("正在刷新数据...")
|
||||||
|
GetIndustryMoneyRankSina(fenlei.value,sort.value).then(result => {
|
||||||
|
if(result.length>0){
|
||||||
|
dataList.value = result
|
||||||
|
//console.log(result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<n-table striped size="small">
|
||||||
|
<n-thead>
|
||||||
|
<n-tr>
|
||||||
|
<n-th>板块名称</n-th>
|
||||||
|
<n-th>涨跌幅</n-th>
|
||||||
|
<n-th>流入资金/万</n-th>
|
||||||
|
<n-th>流出资金/万</n-th>
|
||||||
|
<n-th>净流入/万<n-icon v-if="sort==='0'" :component="CaretDown"/><n-icon v-if="sort==='1'" :component="CaretUp"/></n-th>
|
||||||
|
<n-th>净流入率</n-th>
|
||||||
|
<n-th>领涨股</n-th>
|
||||||
|
<n-th>涨跌幅</n-th>
|
||||||
|
<n-th>最新价</n-th>
|
||||||
|
<n-th>净流入率</n-th>
|
||||||
|
</n-tr>
|
||||||
|
</n-thead>
|
||||||
|
<n-tbody>
|
||||||
|
<n-tr v-for="item in dataList" :key="item.category">
|
||||||
|
<n-td><n-tag :bordered=false type="info">{{item.name}}</n-tag></n-td>
|
||||||
|
<n-td> <n-text :type="item.avg_changeratio>0?'error':'success'">{{(item.avg_changeratio*100).toFixed(2)}}%</n-text></n-td>
|
||||||
|
<n-td><n-text type="info">{{(item.inamount/10000).toFixed(2)}}</n-text></n-td>
|
||||||
|
<n-td><n-text type="info">{{(item.outamount/10000).toFixed(2)}}</n-text></n-td>
|
||||||
|
<n-td><n-text :type="item.netamount>0?'error':'success'">{{(item.netamount/10000).toFixed(2)}}</n-text></n-td>
|
||||||
|
<n-td><n-text :type="item.ratioamount>0?'error':'success'">{{(item.ratioamount*100).toFixed(2)}}%</n-text></n-td>
|
||||||
|
<n-td>
|
||||||
|
<!-- <n-text type="info">{{item.ts_name}}</n-text>-->
|
||||||
|
<n-popover trigger="hover" placement="right">
|
||||||
|
<template #trigger>
|
||||||
|
<n-button tag="a" text :type="item.ts_changeratio>0?'error':'success'" :bordered=false >{{ item.ts_name }}</n-button>
|
||||||
|
</template>
|
||||||
|
<k-line-chart style="width: 800px" :code="item.ts_symbol" :chart-height="500" :name="item.ts_name" :k-days="20" :dark-theme="true"></k-line-chart>
|
||||||
|
</n-popover>
|
||||||
|
</n-td>
|
||||||
|
<n-td><n-text :type="item.ts_changeratio>0?'error':'success'">{{(item.ts_changeratio*100).toFixed(2)}}%</n-text></n-td>
|
||||||
|
<n-td><n-text type="info">{{item.ts_trade}}</n-text></n-td>
|
||||||
|
<n-td><n-text :type="item.ts_ratioamount>0?'error':'success'">{{(item.ts_ratioamount*100).toFixed(2)}}%</n-text></n-td>
|
||||||
|
</n-tr>
|
||||||
|
</n-tbody>
|
||||||
|
</n-table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
@ -2,21 +2,37 @@
|
|||||||
import {computed, h, onBeforeMount, onBeforeUnmount, ref} from 'vue'
|
import {computed, h, onBeforeMount, onBeforeUnmount, ref} from 'vue'
|
||||||
import {
|
import {
|
||||||
GetAIResponseResult,
|
GetAIResponseResult,
|
||||||
GetConfig, GetIndustryRank, GetPromptTemplates,
|
GetConfig,
|
||||||
|
GetIndustryRank,
|
||||||
|
GetPromptTemplates,
|
||||||
GetTelegraphList,
|
GetTelegraphList,
|
||||||
GlobalStockIndexes, ReFleshTelegraphList,
|
GlobalStockIndexes,
|
||||||
SaveAIResponseResult, SaveAsMarkdown, ShareAnalysis,
|
ReFleshTelegraphList,
|
||||||
|
SaveAIResponseResult,
|
||||||
|
SaveAsMarkdown,
|
||||||
|
ShareAnalysis,
|
||||||
SummaryStockNews
|
SummaryStockNews
|
||||||
} from "../../wailsjs/go/main/App";
|
} from "../../wailsjs/go/main/App";
|
||||||
import {EventsOff, EventsOn} from "../../wailsjs/runtime";
|
import {EventsOff, EventsOn} from "../../wailsjs/runtime";
|
||||||
import NewsList from "./newsList.vue";
|
import NewsList from "./newsList.vue";
|
||||||
import KLineChart from "./KLineChart.vue";
|
import KLineChart from "./KLineChart.vue";
|
||||||
import {Add, CaretDown, CaretUp, ChatboxOutline, PulseOutline,} from "@vicons/ionicons5";
|
import { CaretDown, CaretUp, PulseOutline,} from "@vicons/ionicons5";
|
||||||
import {NAvatar, NButton, NFlex, NText, useMessage, useNotification} from "naive-ui";
|
import {NAvatar, NButton, NFlex, NText, useMessage, useNotification} from "naive-ui";
|
||||||
import {ExportPDF} from "@vavt/v3-extension";
|
import {MdPreview} from "md-editor-v3";
|
||||||
import {MdEditor, MdPreview} from "md-editor-v3";
|
import {useRoute} from 'vue-router'
|
||||||
import { useRoute } from 'vue-router'
|
|
||||||
import RankTable from "./rankTable.vue";
|
import RankTable from "./rankTable.vue";
|
||||||
|
import IndustryMoneyRank from "./industryMoneyRank.vue";
|
||||||
|
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";
|
||||||
|
import InvestCalendarTimeLine from "./InvestCalendarTimeLine.vue";
|
||||||
|
import ClsCalendarTimeLine from "./ClsCalendarTimeLine.vue";
|
||||||
|
import SelectStock from "./SelectStock.vue";
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const icon = ref('https://raw.githubusercontent.com/ArvinLovegood/go-stock/master/build/appicon.png');
|
const icon = ref('https://raw.githubusercontent.com/ArvinLovegood/go-stock/master/build/appicon.png');
|
||||||
|
|
||||||
@ -26,35 +42,34 @@ const panelHeight = ref(window.innerHeight - 240)
|
|||||||
|
|
||||||
const telegraphList = ref([])
|
const telegraphList = ref([])
|
||||||
const sinaNewsList = ref([])
|
const sinaNewsList = ref([])
|
||||||
|
|
||||||
const common = ref([])
|
const common = ref([])
|
||||||
const america = ref([])
|
const america = ref([])
|
||||||
const europe = ref([])
|
const europe = ref([])
|
||||||
const asia = ref([])
|
const asia = ref([])
|
||||||
const other = ref([])
|
const other = ref([])
|
||||||
const globalStockIndexes = ref(null)
|
const globalStockIndexes = ref(null)
|
||||||
const summaryModal= ref(false)
|
const summaryModal = ref(false)
|
||||||
const summaryBTN= ref(true)
|
const summaryBTN = ref(true)
|
||||||
const darkTheme= ref(false)
|
const darkTheme = ref(false)
|
||||||
const theme=computed(() => {
|
const theme = computed(() => {
|
||||||
return darkTheme ? 'dark' : 'light'
|
return darkTheme ? 'dark' : 'light'
|
||||||
})
|
})
|
||||||
const aiSummary=ref(``)
|
const aiSummary = ref(``)
|
||||||
const aiSummaryTime=ref("")
|
const aiSummaryTime = ref("")
|
||||||
const modelName=ref("")
|
const modelName = ref("")
|
||||||
const chatId=ref("")
|
const chatId = ref("")
|
||||||
const question=ref(``)
|
const question = ref(``)
|
||||||
const sysPromptId=ref(0)
|
const sysPromptId = ref(0)
|
||||||
const loading=ref(true)
|
const loading = ref(true)
|
||||||
const sysPromptOptions=ref([])
|
const sysPromptOptions = ref([])
|
||||||
const userPromptOptions=ref([])
|
const userPromptOptions = ref([])
|
||||||
const promptTemplates=ref([])
|
const promptTemplates = ref([])
|
||||||
const industryRanks=ref([])
|
const industryRanks = ref([])
|
||||||
const sort = ref("")
|
const sort = ref("0")
|
||||||
const sortIcon= ref(h(CaretDown))
|
const nowTab = ref("市场快讯")
|
||||||
const nowTab=ref("市场快讯")
|
const indexInterval = ref(null)
|
||||||
const indexInterval= ref(null)
|
const indexIndustryRank = ref(null)
|
||||||
const indexIndustryRank= ref(null)
|
const stockCode= ref('')
|
||||||
|
|
||||||
function getIndex() {
|
function getIndex() {
|
||||||
GlobalStockIndexes().then((res) => {
|
GlobalStockIndexes().then((res) => {
|
||||||
@ -67,16 +82,19 @@ function getIndex() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
nowTab.value=route.query.name
|
nowTab.value = route.query.name
|
||||||
|
stockCode.value = route.query.stockCode
|
||||||
GetConfig().then(result => {
|
GetConfig().then(result => {
|
||||||
summaryBTN.value= result.openAiEnable
|
summaryBTN.value = result.openAiEnable
|
||||||
darkTheme.value = result.darkTheme
|
darkTheme.value = result.darkTheme
|
||||||
})
|
})
|
||||||
GetPromptTemplates("","").then(res=>{
|
GetPromptTemplates("", "").then(res => {
|
||||||
promptTemplates.value=res
|
promptTemplates.value = res
|
||||||
sysPromptOptions.value=promptTemplates.value.filter(item => item.type === '模型系统Prompt')
|
sysPromptOptions.value = promptTemplates.value.filter(item => item.type === '模型系统Prompt')
|
||||||
userPromptOptions.value=promptTemplates.value.filter(item => item.type === '模型用户Prompt')
|
userPromptOptions.value = promptTemplates.value.filter(item => item.type === '模型用户Prompt')
|
||||||
})
|
})
|
||||||
|
|
||||||
GetTelegraphList("财联社电报").then((res) => {
|
GetTelegraphList("财联社电报").then((res) => {
|
||||||
@ -87,13 +105,13 @@ onBeforeMount(() => {
|
|||||||
})
|
})
|
||||||
getIndex();
|
getIndex();
|
||||||
industryRank();
|
industryRank();
|
||||||
indexInterval.value=setInterval(() => {
|
indexInterval.value = setInterval(() => {
|
||||||
getIndex()
|
getIndex()
|
||||||
}, 3000)
|
}, 3000)
|
||||||
|
|
||||||
indexIndustryRank.value=setInterval(() => {
|
indexIndustryRank.value = setInterval(() => {
|
||||||
industryRank()
|
industryRank()
|
||||||
},1000*10)
|
}, 1000 * 10)
|
||||||
})
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
@ -105,7 +123,7 @@ onBeforeUnmount(() => {
|
|||||||
clearInterval(indexIndustryRank.value)
|
clearInterval(indexIndustryRank.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
EventsOn("changeMarketTab" ,async (msg) => {
|
EventsOn("changeMarketTab", async (msg) => {
|
||||||
//message.info(msg.name)
|
//message.info(msg.name)
|
||||||
updateTab(msg.name)
|
updateTab(msg.name)
|
||||||
})
|
})
|
||||||
@ -128,7 +146,7 @@ window.onresize = () => {
|
|||||||
panelHeight.value = window.innerHeight - 240
|
panelHeight.value = window.innerHeight - 240
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAreaName(code){
|
function getAreaName(code) {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case "america":
|
case "america":
|
||||||
return "美洲"
|
return "美洲"
|
||||||
@ -142,35 +160,42 @@ function getAreaName(code){
|
|||||||
return "其他"
|
return "其他"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function industryRank(){
|
|
||||||
if(sort.value==="0"){
|
function changeIndustryRankSort() {
|
||||||
sort.value="1"
|
if (sort.value === "0") {
|
||||||
}else{
|
sort.value = "1"
|
||||||
sort.value="0"
|
} else {
|
||||||
|
sort.value = "0"
|
||||||
}
|
}
|
||||||
GetIndustryRank(sort.value,150).then(result => {
|
industryRank()
|
||||||
if(result.length>0){
|
}
|
||||||
console.log(result)
|
|
||||||
|
function industryRank() {
|
||||||
|
|
||||||
|
GetIndustryRank(sort.value, 150).then(result => {
|
||||||
|
if (result.length > 0) {
|
||||||
|
//console.log(result)
|
||||||
industryRanks.value = result
|
industryRanks.value = result
|
||||||
}else{
|
} else {
|
||||||
message.info("暂无数据")
|
message.info("暂无数据")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function reAiSummary(){
|
function reAiSummary() {
|
||||||
aiSummary.value=""
|
aiSummary.value = ""
|
||||||
summaryModal.value = true
|
summaryModal.value = true
|
||||||
loading.value = true
|
loading.value = true
|
||||||
SummaryStockNews(question.value,sysPromptId.value)
|
SummaryStockNews(question.value, sysPromptId.value)
|
||||||
}
|
}
|
||||||
function getAiSummary(){
|
|
||||||
|
function getAiSummary() {
|
||||||
summaryModal.value = true
|
summaryModal.value = true
|
||||||
loading.value = true
|
loading.value = true
|
||||||
GetAIResponseResult("市场资讯").then(result => {
|
GetAIResponseResult("市场资讯").then(result => {
|
||||||
if(result.content){
|
if (result.content) {
|
||||||
aiSummary.value=result.content
|
aiSummary.value = result.content
|
||||||
question.value=result.question
|
question.value = result.question
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
|
||||||
const date = new Date(result.CreatedAt);
|
const date = new Date(result.CreatedAt);
|
||||||
@ -180,13 +205,13 @@ function getAiSummary(){
|
|||||||
const hours = String(date.getHours()).padStart(2, '0');
|
const hours = String(date.getHours()).padStart(2, '0');
|
||||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||||
aiSummaryTime.value=`${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
aiSummaryTime.value = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||||
modelName.value=result.modelName
|
modelName.value = result.modelName
|
||||||
}else{
|
} else {
|
||||||
aiSummaryTime.value=""
|
aiSummaryTime.value = ""
|
||||||
aiSummary.value=""
|
aiSummary.value = ""
|
||||||
modelName.value=""
|
modelName.value = ""
|
||||||
SummaryStockNews(question.value,sysPromptId.value)
|
SummaryStockNews(question.value, sysPromptId.value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -196,31 +221,31 @@ function updateTab(name) {
|
|||||||
nowTab.value = name
|
nowTab.value = name
|
||||||
}
|
}
|
||||||
|
|
||||||
EventsOn("summaryStockNews",async (msg) => {
|
EventsOn("summaryStockNews", async (msg) => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
////console.log(msg)
|
////console.log(msg)
|
||||||
if (msg === "DONE") {
|
if (msg === "DONE") {
|
||||||
SaveAIResponseResult("市场资讯","市场资讯", aiSummary.value, chatId.value,question.value)
|
SaveAIResponseResult("市场资讯", "市场资讯", aiSummary.value, chatId.value, question.value)
|
||||||
message.info("AI分析完成!")
|
message.info("AI分析完成!")
|
||||||
message.destroyAll()
|
message.destroyAll()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if(msg.chatId){
|
if (msg.chatId) {
|
||||||
chatId.value = msg.chatId
|
chatId.value = msg.chatId
|
||||||
}
|
}
|
||||||
if(msg.question){
|
if (msg.question) {
|
||||||
question.value = msg.question
|
question.value = msg.question
|
||||||
}
|
}
|
||||||
if(msg.content){
|
if (msg.content) {
|
||||||
aiSummary.value =aiSummary.value + msg.content
|
aiSummary.value = aiSummary.value + msg.content
|
||||||
}
|
}
|
||||||
if(msg.extraContent){
|
if (msg.extraContent) {
|
||||||
aiSummary.value = aiSummary.value + msg.extraContent
|
aiSummary.value = aiSummary.value + msg.extraContent
|
||||||
}
|
}
|
||||||
if(msg.model){
|
if (msg.model) {
|
||||||
modelName.value=msg.model
|
modelName.value = msg.model
|
||||||
}
|
}
|
||||||
if(msg.time){
|
if (msg.time) {
|
||||||
aiSummaryTime.value = msg.time
|
aiSummaryTime.value = msg.time
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -234,13 +259,15 @@ async function copyToClipboard() {
|
|||||||
message.error('复制失败: ' + err);
|
message.error('复制失败: ' + err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function saveAsMarkdown(){
|
|
||||||
SaveAsMarkdown('市场资讯','市场资讯').then(result => {
|
function saveAsMarkdown() {
|
||||||
|
SaveAsMarkdown('市场资讯', '市场资讯').then(result => {
|
||||||
message.success(result)
|
message.success(result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function share(){
|
|
||||||
ShareAnalysis('市场资讯','市场资讯').then(msg => {
|
function share() {
|
||||||
|
ShareAnalysis('市场资讯', '市场资讯').then(msg => {
|
||||||
//message.info(msg)
|
//message.info(msg)
|
||||||
notify.info({
|
notify.info({
|
||||||
avatar: () =>
|
avatar: () =>
|
||||||
@ -250,26 +277,26 @@ function share(){
|
|||||||
src: icon.value
|
src: icon.value
|
||||||
}),
|
}),
|
||||||
title: '分享到社区',
|
title: '分享到社区',
|
||||||
duration:1000*30,
|
duration: 1000 * 30,
|
||||||
content: () => {
|
content: () => {
|
||||||
return h('div', {
|
return h('div', {
|
||||||
style: {
|
style: {
|
||||||
'text-align': 'left',
|
'text-align': 'left',
|
||||||
'font-size': '14px',
|
'font-size': '14px',
|
||||||
}
|
}
|
||||||
}, { default: () => msg })
|
}, {default: () => msg})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReFlesh(source){
|
function ReFlesh(source) {
|
||||||
console.log("ReFlesh:",source)
|
//console.log("ReFlesh:", source)
|
||||||
ReFleshTelegraphList(source).then(res => {
|
ReFleshTelegraphList(source).then(res => {
|
||||||
if(source==="财联社电报"){
|
if (source === "财联社电报") {
|
||||||
telegraphList.value = res
|
telegraphList.value = res
|
||||||
}
|
}
|
||||||
if(source==="新浪财经"){
|
if (source === "新浪财经") {
|
||||||
sinaNewsList.value = res
|
sinaNewsList.value = res
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -278,8 +305,8 @@ function ReFlesh(source){
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<n-card>
|
<n-card>
|
||||||
<n-tabs type="line" animated @update-value="updateTab" :value="nowTab" >
|
<n-tabs type="line" animated @update-value="updateTab" :value="nowTab">
|
||||||
<n-tab-pane name="市场快讯" tab="市场快讯" >
|
<n-tab-pane name="市场快讯" tab="市场快讯">
|
||||||
<n-grid :cols="2" :y-gap="0">
|
<n-grid :cols="2" :y-gap="0">
|
||||||
<n-gi>
|
<n-gi>
|
||||||
<news-list :newsList="telegraphList" :header-title="'财联社电报'" @update:message="ReFlesh"></news-list>
|
<news-list :newsList="telegraphList" :header-title="'财联社电报'" @update:message="ReFlesh"></news-list>
|
||||||
@ -302,11 +329,16 @@ function ReFlesh(source){
|
|||||||
<n-grid :cols="3" :y-gap="0">
|
<n-grid :cols="3" :y-gap="0">
|
||||||
<n-gi>
|
<n-gi>
|
||||||
|
|
||||||
<n-text :type="item.zdf>0?'error':'success'"><n-image :src="item.img" width="20"/> {{ item.name }}</n-text>
|
<n-text :type="item.zdf>0?'error':'success'">
|
||||||
|
<n-image :src="item.img" width="20"/> {{ item.name }}
|
||||||
|
</n-text>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi>
|
<n-gi>
|
||||||
<n-text :type="item.zdf>0?'error':'success'">{{ item.zxj }}</n-text>
|
<n-text :type="item.zdf>0?'error':'success'">{{ item.zxj }}</n-text>
|
||||||
<n-text :type="item.zdf>0?'error':'success'"><n-number-animation :precision="2" :from="0" :to="item.zdf" />%</n-text>
|
<n-text :type="item.zdf>0?'error':'success'">
|
||||||
|
<n-number-animation :precision="2" :from="0" :to="item.zdf"/>
|
||||||
|
%
|
||||||
|
</n-text>
|
||||||
|
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-gi>
|
<n-gi>
|
||||||
@ -388,30 +420,105 @@ function ReFlesh(source){
|
|||||||
</n-tabs>
|
</n-tabs>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
<n-tab-pane name="行业排名" tab="行业排名">
|
<n-tab-pane name="行业排名" tab="行业排名">
|
||||||
<n-table striped>
|
<n-tabs type="card" animated>
|
||||||
<n-thead>
|
<n-tab-pane name="行业涨幅排名" tab="行业涨幅排名">
|
||||||
<n-tr>
|
<n-table striped>
|
||||||
<n-th>行业名称</n-th>
|
<n-thead>
|
||||||
<n-th @click="industryRank">行业涨幅<n-icon v-if="sort==='0'" :component="CaretDown"/><n-icon v-if="sort==='1'" :component="CaretUp"/></n-th>
|
<n-tr>
|
||||||
<n-th>行业5日涨幅</n-th>
|
<n-th>行业名称</n-th>
|
||||||
<n-th>行业20日涨幅</n-th>
|
<n-th @click="changeIndustryRankSort">行业涨幅
|
||||||
<n-th>领涨股</n-th>
|
<n-icon v-if="sort==='0'" :component="CaretDown"/>
|
||||||
<n-th>涨幅</n-th>
|
<n-icon v-if="sort==='1'" :component="CaretUp"/>
|
||||||
<n-th>最新价</n-th>
|
</n-th>
|
||||||
</n-tr>
|
<n-th>行业5日涨幅</n-th>
|
||||||
</n-thead>
|
<n-th>行业20日涨幅</n-th>
|
||||||
<n-tbody>
|
<n-th>领涨股</n-th>
|
||||||
<n-tr v-for="item in industryRanks" :key="item.bd_code">
|
<n-th>涨幅</n-th>
|
||||||
<n-td><n-tag :bordered=false type="info">{{ item.bd_name }}</n-tag></n-td>
|
<n-th>最新价</n-th>
|
||||||
<n-td><n-text :type="item.bd_zdf>0?'error':'success'">{{item.bd_zdf}}%</n-text></n-td>
|
</n-tr>
|
||||||
<n-td><n-text :type="item.bd_zdf5>0?'error':'success'">{{item.bd_zdf5}}%</n-text></n-td>
|
</n-thead>
|
||||||
<n-td><n-text :type="item.bd_zdf20>0?'error':'success'">{{item.bd_zdf20}}%</n-text></n-td>
|
<n-tbody>
|
||||||
<n-td><n-text :type="item.nzg_zdf>0?'error':'success'"> {{item.nzg_name}} <n-text type="info">{{item.nzg_code}}</n-text></n-text></n-td>
|
<n-tr v-for="item in industryRanks" :key="item.bd_code">
|
||||||
<n-td><n-text :type="item.nzg_zdf>0?'error':'success'"> {{item.nzg_zdf}}%</n-text></n-td>
|
<n-td>
|
||||||
<n-td> <n-text :type="item.nzg_zdf>0?'error':'success'">{{item.nzg_zxj}}</n-text></n-td>
|
<n-tag :bordered=false type="info">{{ item.bd_name }}</n-tag>
|
||||||
</n-tr>
|
</n-td>
|
||||||
</n-tbody>
|
<n-td>
|
||||||
</n-table>
|
<n-text :type="item.bd_zdf>0?'error':'success'">{{ item.bd_zdf }}%</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.bd_zdf5>0?'error':'success'">{{ item.bd_zdf5 }}%</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.bd_zdf20>0?'error':'success'">{{ item.bd_zdf20 }}%</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.nzg_zdf>0?'error':'success'"> {{ item.nzg_name }}
|
||||||
|
<n-text type="info">{{ item.nzg_code }}</n-text>
|
||||||
|
</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.nzg_zdf>0?'error':'success'"> {{ item.nzg_zdf }}%</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.nzg_zdf>0?'error':'success'">{{ item.nzg_zxj }}</n-text>
|
||||||
|
</n-td>
|
||||||
|
</n-tr>
|
||||||
|
</n-tbody>
|
||||||
|
</n-table>
|
||||||
|
<n-table striped>
|
||||||
|
<n-thead>
|
||||||
|
<n-tr>
|
||||||
|
<n-th>行业名称</n-th>
|
||||||
|
<n-th @click="changeIndustryRankSort">行业涨幅
|
||||||
|
<n-icon v-if="sort==='0'" :component="CaretDown"/>
|
||||||
|
<n-icon v-if="sort==='1'" :component="CaretUp"/>
|
||||||
|
</n-th>
|
||||||
|
<n-th>行业5日涨幅</n-th>
|
||||||
|
<n-th>行业20日涨幅</n-th>
|
||||||
|
<n-th>领涨股</n-th>
|
||||||
|
<n-th>涨幅</n-th>
|
||||||
|
<n-th>最新价</n-th>
|
||||||
|
</n-tr>
|
||||||
|
</n-thead>
|
||||||
|
<n-tbody>
|
||||||
|
<n-tr v-for="item in industryRanks" :key="item.bd_code">
|
||||||
|
<n-td>
|
||||||
|
<n-tag :bordered=false type="info">{{ item.bd_name }}</n-tag>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.bd_zdf>0?'error':'success'">{{ item.bd_zdf }}%</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.bd_zdf5>0?'error':'success'">{{ item.bd_zdf5 }}%</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.bd_zdf20>0?'error':'success'">{{ item.bd_zdf20 }}%</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.nzg_zdf>0?'error':'success'"> {{ item.nzg_name }}
|
||||||
|
<n-text type="info">{{ item.nzg_code }}</n-text>
|
||||||
|
</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.nzg_zdf>0?'error':'success'"> {{ item.nzg_zdf }}%</n-text>
|
||||||
|
</n-td>
|
||||||
|
<n-td>
|
||||||
|
<n-text :type="item.nzg_zdf>0?'error':'success'">{{ item.nzg_zxj }}</n-text>
|
||||||
|
</n-td>
|
||||||
|
</n-tr>
|
||||||
|
</n-tbody>
|
||||||
|
</n-table>
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="行业资金排名(净流入)" tab="行业资金排名">
|
||||||
|
<industryMoneyRank :fenlei="'0'" :header-title="'行业资金排名(净流入)'" :sort="'netamount'"/>
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="证监会行业资金排名(净流入)" tab="证监会行业资金排名">
|
||||||
|
<industryMoneyRank :fenlei="'2'" :header-title="'证监会行业资金排名(净流入)'" :sort="'netamount'"/>
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="概念板块资金排名(净流入)" tab="概念板块资金排名">
|
||||||
|
<industryMoneyRank :fenlei="'1'" :header-title="'概念板块资金排名(净流入)'" :sort="'netamount'"/>
|
||||||
|
</n-tab-pane>
|
||||||
|
</n-tabs>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
<n-tab-pane name="个股资金流向" tab="个股资金流向">
|
<n-tab-pane name="个股资金流向" tab="个股资金流向">
|
||||||
<n-tabs type="card" animated>
|
<n-tabs type="card" animated>
|
||||||
@ -444,28 +551,78 @@ function ReFlesh(source){
|
|||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
</n-tabs>
|
</n-tabs>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="龙虎榜" tab="龙虎榜">
|
||||||
|
<LongTigerRankList />
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="个股研报" tab="个股研报">
|
||||||
|
<StockResearchReportList :stock-code="stockCode"/>
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="公司公告" tab="公司公告 ">
|
||||||
|
<StockNoticeList :stock-code="stockCode" />
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="行业研究" tab="行业研究 ">
|
||||||
|
<IndustryResearchReportList/>
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="当前热门" tab="当前热门">
|
||||||
|
<n-tabs type="card" animated>
|
||||||
|
<n-tab-pane name="全球" tab="全球">
|
||||||
|
<HotStockList :market-type="'10'"/>
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="沪深" tab="沪深">
|
||||||
|
<HotStockList :market-type="'12'"/>
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="港股" tab="港股">
|
||||||
|
<HotStockList :market-type="'13'"/>
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="美股" tab="美股">
|
||||||
|
<HotStockList :market-type="'11'"/>
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="热门话题" tab="热门话题">
|
||||||
|
<n-grid :cols="1" :y-gap="10">
|
||||||
|
<n-grid-item>
|
||||||
|
<HotTopics/>
|
||||||
|
</n-grid-item>
|
||||||
|
<!-- <n-grid-item>-->
|
||||||
|
<!-- <HotEvents/>-->
|
||||||
|
<!-- </n-grid-item>-->
|
||||||
|
</n-grid>
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="重大事件时间轴" tab="重大事件时间轴">
|
||||||
|
<InvestCalendarTimeLine />
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="财经日历" tab="财经日历">
|
||||||
|
<ClsCalendarTimeLine />
|
||||||
|
</n-tab-pane>
|
||||||
|
</n-tabs>
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="指标选股" tab="指标选股">
|
||||||
|
<select-stock />
|
||||||
|
</n-tab-pane>
|
||||||
</n-tabs>
|
</n-tabs>
|
||||||
</n-card>
|
</n-card>
|
||||||
<n-modal transform-origin="center" v-model:show="summaryModal" preset="card" style="width: 800px;" :title="'AI市场资讯总结'" >
|
<n-modal transform-origin="center" v-model:show="summaryModal" preset="card" style="width: 800px;"
|
||||||
|
:title="'AI市场资讯总结'">
|
||||||
<n-spin size="small" :show="loading">
|
<n-spin size="small" :show="loading">
|
||||||
<MdPreview style="height: 440px;text-align: left" :modelValue="aiSummary" :theme="theme"/>
|
<MdPreview style="height: 440px;text-align: left" :modelValue="aiSummary" :theme="theme"/>
|
||||||
</n-spin>
|
</n-spin>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<n-flex justify="space-between" ref="tipsRef">
|
<n-flex justify="space-between" ref="tipsRef">
|
||||||
<n-text type="info" v-if="aiSummaryTime" >
|
<n-text type="info" v-if="aiSummaryTime">
|
||||||
<n-tag v-if="modelName" type="warning" round :title="chatId" :bordered="false">{{modelName}}</n-tag>
|
<n-tag v-if="modelName" type="warning" round :title="chatId" :bordered="false">{{ modelName }}</n-tag>
|
||||||
{{aiSummaryTime}}
|
{{ aiSummaryTime }}
|
||||||
</n-text>
|
</n-text>
|
||||||
<n-text type="error" >*AI分析结果仅供参考,请以实际行情为准。投资需谨慎,风险自担。</n-text>
|
<n-text type="error">*AI分析结果仅供参考,请以实际行情为准。投资需谨慎,风险自担。</n-text>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</template>
|
</template>
|
||||||
<template #action>
|
<template #action>
|
||||||
<n-flex justify="space-between" style="margin-bottom: 10px">
|
<n-flex justify="space-between" style="margin-bottom: 10px">
|
||||||
<n-select style="width: 49%" v-model:value="sysPromptId" label-field="name" value-field="ID" :options="sysPromptOptions" placeholder="请选择系统提示词" />
|
<n-select style="width: 49%" v-model:value="sysPromptId" label-field="name" value-field="ID"
|
||||||
<n-select style="width: 49%" v-model:value="question" label-field="name" value-field="content" :options="userPromptOptions" placeholder="请选择用户提示词" />
|
:options="sysPromptOptions" placeholder="请选择系统提示词"/>
|
||||||
|
<n-select style="width: 49%" v-model:value="question" label-field="name" value-field="content"
|
||||||
|
:options="userPromptOptions" placeholder="请选择用户提示词"/>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
<n-flex justify="right">
|
<n-flex justify="right">
|
||||||
<n-input v-model:value="question" style="text-align: left" clearable
|
<n-input v-model:value="question" style="text-align: left" clearable
|
||||||
type="textarea"
|
type="textarea"
|
||||||
:show-count="true"
|
:show-count="true"
|
||||||
placeholder="请输入您的问题:例如 总结和分析股票市场新闻中的投资机会"
|
placeholder="请输入您的问题:例如 总结和分析股票市场新闻中的投资机会"
|
||||||
@ -474,7 +631,7 @@ function ReFlesh(source){
|
|||||||
maxRows: 5
|
maxRows: 5
|
||||||
}"
|
}"
|
||||||
/>
|
/>
|
||||||
<n-button size="tiny" type="warning" @click="reAiSummary">再次总结</n-button>
|
<n-button size="tiny" type="warning" @click="reAiSummary">再次总结</n-button>
|
||||||
<n-button size="tiny" type="success" @click="copyToClipboard">复制到剪切板</n-button>
|
<n-button size="tiny" type="success" @click="copyToClipboard">复制到剪切板</n-button>
|
||||||
<n-button size="tiny" type="primary" @click="saveAsMarkdown">保存为Markdown文件</n-button>
|
<n-button size="tiny" type="primary" @click="saveAsMarkdown">保存为Markdown文件</n-button>
|
||||||
<n-button size="tiny" type="error" @click="share">分享到项目社区</n-button>
|
<n-button size="tiny" type="error" @click="share">分享到项目社区</n-button>
|
||||||
@ -483,13 +640,15 @@ function ReFlesh(source){
|
|||||||
</n-modal>
|
</n-modal>
|
||||||
|
|
||||||
<div style="position: fixed;bottom: 18px;right:25px;z-index: 10;" v-if="summaryBTN">
|
<div style="position: fixed;bottom: 18px;right:25px;z-index: 10;" v-if="summaryBTN">
|
||||||
<n-input-group >
|
<n-input-group>
|
||||||
<n-button type="primary" @click="getAiSummary">
|
<n-button type="primary" @click="getAiSummary">
|
||||||
<n-icon :component="PulseOutline"/> AI总结
|
<n-icon :component="PulseOutline"/> AI总结
|
||||||
</n-button>
|
</n-button>
|
||||||
</n-input-group>
|
</n-input-group>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
|
@ -68,10 +68,17 @@ const handleLine = (code, days) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
console.log("volume", volume)
|
//console.log("volume", volume)
|
||||||
const upColor = '#ec0000';
|
const upColor = '#ec0000';
|
||||||
const downColor = '#00da3c';
|
const downColor = '#00da3c';
|
||||||
let option = {
|
let option = {
|
||||||
|
title: {
|
||||||
|
text: name,
|
||||||
|
left: '20px',
|
||||||
|
textStyle: {
|
||||||
|
color: darkTheme?'#ccc':'#456'
|
||||||
|
}
|
||||||
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
|
@ -45,6 +45,7 @@ const formValue = ref({
|
|||||||
enableNews:false,
|
enableNews:false,
|
||||||
darkTheme:true,
|
darkTheme:true,
|
||||||
enableFund:false,
|
enableFund:false,
|
||||||
|
enablePushNews:false,
|
||||||
})
|
})
|
||||||
const promptTemplates=ref([])
|
const promptTemplates=ref([])
|
||||||
onMounted(()=>{
|
onMounted(()=>{
|
||||||
@ -78,13 +79,14 @@ onMounted(()=>{
|
|||||||
formValue.value.enableNews = res.enableNews
|
formValue.value.enableNews = res.enableNews
|
||||||
formValue.value.darkTheme = res.darkTheme
|
formValue.value.darkTheme = res.darkTheme
|
||||||
formValue.value.enableFund = res.enableFund
|
formValue.value.enableFund = res.enableFund
|
||||||
|
formValue.value.enablePushNews = res.enablePushNews
|
||||||
|
|
||||||
console.log(res)
|
//console.log(res)
|
||||||
})
|
})
|
||||||
//message.info("加载完成")
|
//message.info("加载完成")
|
||||||
|
|
||||||
GetPromptTemplates("","").then(res=>{
|
GetPromptTemplates("","").then(res=>{
|
||||||
console.log(res)
|
//console.log(res)
|
||||||
promptTemplates.value=res
|
promptTemplates.value=res
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -118,6 +120,7 @@ function saveConfig(){
|
|||||||
enableNews:formValue.value.enableNews,
|
enableNews:formValue.value.enableNews,
|
||||||
darkTheme:formValue.value.darkTheme,
|
darkTheme:formValue.value.darkTheme,
|
||||||
enableFund:formValue.value.enableFund,
|
enableFund:formValue.value.enableFund,
|
||||||
|
enablePushNews:formValue.value.enablePushNews
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -165,7 +168,7 @@ function importConfig(){
|
|||||||
let reader = new FileReader();
|
let reader = new FileReader();
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
let config = JSON.parse(e.target.result);
|
let config = JSON.parse(e.target.result);
|
||||||
console.log(config)
|
//console.log(config)
|
||||||
formValue.value.ID = config.ID
|
formValue.value.ID = config.ID
|
||||||
formValue.value.tushareToken = config.tushareToken
|
formValue.value.tushareToken = config.tushareToken
|
||||||
formValue.value.dingPush = {
|
formValue.value.dingPush = {
|
||||||
@ -195,6 +198,7 @@ function importConfig(){
|
|||||||
formValue.value.enableNews = config.enableNews
|
formValue.value.enableNews = config.enableNews
|
||||||
formValue.value.darkTheme = config.darkTheme
|
formValue.value.darkTheme = config.darkTheme
|
||||||
formValue.value.enableFund = config.enableFund
|
formValue.value.enableFund = config.enableFund
|
||||||
|
formValue.value.enablePushNews = config.enablePushNews
|
||||||
// formRef.value.resetFields()
|
// formRef.value.resetFields()
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
@ -204,7 +208,7 @@ function importConfig(){
|
|||||||
|
|
||||||
|
|
||||||
window.onerror = function (event, source, lineno, colno, error) {
|
window.onerror = function (event, source, lineno, colno, error) {
|
||||||
console.log(event, source, lineno, colno, error)
|
//console.log(event, source, lineno, colno, error)
|
||||||
// 将错误信息发送给后端
|
// 将错误信息发送给后端
|
||||||
EventsEmit("frontendError", {
|
EventsEmit("frontendError", {
|
||||||
page: "settings.vue",
|
page: "settings.vue",
|
||||||
@ -237,14 +241,14 @@ function savePrompt(){
|
|||||||
AddPrompt(formPrompt.value).then(res=>{
|
AddPrompt(formPrompt.value).then(res=>{
|
||||||
message.success(res)
|
message.success(res)
|
||||||
GetPromptTemplates("","").then(res=>{
|
GetPromptTemplates("","").then(res=>{
|
||||||
console.log(res)
|
//console.log(res)
|
||||||
promptTemplates.value=res
|
promptTemplates.value=res
|
||||||
})
|
})
|
||||||
showManagePromptsModal.value=false
|
showManagePromptsModal.value=false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
function editPrompt(prompt){
|
function editPrompt(prompt){
|
||||||
console.log(prompt)
|
//console.log(prompt)
|
||||||
formPrompt.value.ID=prompt.ID
|
formPrompt.value.ID=prompt.ID
|
||||||
formPrompt.value.Name=prompt.name
|
formPrompt.value.Name=prompt.name
|
||||||
formPrompt.value.Content=prompt.content
|
formPrompt.value.Content=prompt.content
|
||||||
@ -255,7 +259,7 @@ function deletePrompt(ID){
|
|||||||
DelPrompt(ID).then(res=>{
|
DelPrompt(ID).then(res=>{
|
||||||
message.success(res)
|
message.success(res)
|
||||||
GetPromptTemplates("","").then(res=>{
|
GetPromptTemplates("","").then(res=>{
|
||||||
console.log(res)
|
//console.log(res)
|
||||||
promptTemplates.value=res
|
promptTemplates.value=res
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -288,7 +292,7 @@ function deletePrompt(ID){
|
|||||||
<n-form-item-gi :span="10" label="浏览器安装路径:" path="browserPath" >
|
<n-form-item-gi :span="10" label="浏览器安装路径:" path="browserPath" >
|
||||||
<n-input type="text" placeholder="浏览器安装路径" v-model:value="formValue.browserPath" clearable />
|
<n-input type="text" placeholder="浏览器安装路径" v-model:value="formValue.browserPath" clearable />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
<n-form-item-gi :span="6" label="是否启用指数基金:" path="enableFund" >
|
<n-form-item-gi :span="6" label="指数基金:" path="enableFund" >
|
||||||
<n-switch v-model:value="formValue.enableFund" />
|
<n-switch v-model:value="formValue.enableFund" />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
</n-grid>
|
</n-grid>
|
||||||
@ -297,18 +301,21 @@ function deletePrompt(ID){
|
|||||||
<n-gi :span="24">
|
<n-gi :span="24">
|
||||||
<n-text type="success" style="font-size: 25px;font-weight: bold">通知设置</n-text>
|
<n-text type="success" style="font-size: 25px;font-weight: bold">通知设置</n-text>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-form-item-gi :span="6" label="是否启用钉钉推送:" path="dingPush.enable" >
|
<n-form-item-gi :span="4" label="钉钉推送:" path="dingPush.enable" >
|
||||||
<n-switch v-model:value="formValue.dingPush.enable" />
|
<n-switch v-model:value="formValue.dingPush.enable" />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
<n-form-item-gi :span="6" label="是否启用本地推送:" path="localPush.enable" >
|
<n-form-item-gi :span="4" label="本地推送:" path="localPush.enable" >
|
||||||
<n-switch v-model:value="formValue.localPush.enable" />
|
<n-switch v-model:value="formValue.localPush.enable" />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
<n-form-item-gi :span="5" label="弹幕功能:" path="enableDanmu" >
|
<n-form-item-gi :span="4" label="弹幕功能:" path="enableDanmu" >
|
||||||
<n-switch v-model:value="formValue.enableDanmu" />
|
<n-switch v-model:value="formValue.enableDanmu" />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
<n-form-item-gi :span="5" label="是否显示滚动快讯(重启生效):" path="enableNews" >
|
<n-form-item-gi :span="4" label="显示滚动快讯:" path="enableNews" >
|
||||||
<n-switch v-model:value="formValue.enableNews" />
|
<n-switch v-model:value="formValue.enableNews" />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
|
<n-form-item-gi :span="4" label="市场资讯提醒:" path="enablePushNews" >
|
||||||
|
<n-switch v-model:value="formValue.enablePushNews" />
|
||||||
|
</n-form-item-gi>
|
||||||
<n-form-item-gi :span="22" v-if="formValue.dingPush.enable" label="钉钉机器人接口地址:" path="dingPush.dingRobot" >
|
<n-form-item-gi :span="22" v-if="formValue.dingPush.enable" label="钉钉机器人接口地址:" path="dingPush.dingRobot" >
|
||||||
<n-input placeholder="请输入钉钉机器人接口地址" v-model:value="formValue.dingPush.dingRobot"/>
|
<n-input placeholder="请输入钉钉机器人接口地址" v-model:value="formValue.dingPush.dingRobot"/>
|
||||||
<n-button type="primary" @click="sendTestNotice">发送测试通知</n-button>
|
<n-button type="primary" @click="sendTestNotice">发送测试通知</n-button>
|
||||||
@ -319,7 +326,7 @@ function deletePrompt(ID){
|
|||||||
<n-gi :span="24">
|
<n-gi :span="24">
|
||||||
<n-text type="success" style="font-size: 25px;font-weight: bold">OpenAI设置</n-text>
|
<n-text type="success" style="font-size: 25px;font-weight: bold">OpenAI设置</n-text>
|
||||||
</n-gi>
|
</n-gi>
|
||||||
<n-form-item-gi :span="3" label="是否启用AI诊股:" path="openAI.enable" >
|
<n-form-item-gi :span="3" label="AI诊股:" path="openAI.enable" >
|
||||||
<n-switch v-model:value="formValue.openAI.enable" />
|
<n-switch v-model:value="formValue.openAI.enable" />
|
||||||
</n-form-item-gi>
|
</n-form-item-gi>
|
||||||
<n-form-item-gi :span="9" v-if="formValue.openAI.enable" label="openAI 接口地址:" path="openAI.baseUrl" >
|
<n-form-item-gi :span="9" v-if="formValue.openAI.enable" label="openAI 接口地址:" path="openAI.baseUrl" >
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -12,6 +12,7 @@ const routes = [
|
|||||||
{ 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' },
|
{ path: '/market', component: market,name: 'market' },
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
|
28
frontend/wailsjs/go/main/App.d.ts
vendored
28
frontend/wailsjs/go/main/App.d.ts
vendored
@ -11,10 +11,16 @@ export function AddPrompt(arg1:models.Prompt):Promise<string>;
|
|||||||
|
|
||||||
export function AddStockGroup(arg1:number,arg2:string):Promise<string>;
|
export function AddStockGroup(arg1:number,arg2:string):Promise<string>;
|
||||||
|
|
||||||
|
export function AnalyzeSentiment(arg1:string):Promise<data.SentimentResult>;
|
||||||
|
|
||||||
export function CheckUpdate():Promise<void>;
|
export function CheckUpdate():Promise<void>;
|
||||||
|
|
||||||
|
export function ClsCalendar():Promise<Array<any>>;
|
||||||
|
|
||||||
export function DelPrompt(arg1:number):Promise<string>;
|
export function DelPrompt(arg1:number):Promise<string>;
|
||||||
|
|
||||||
|
export function EMDictCode(arg1:string):Promise<Array<any>>;
|
||||||
|
|
||||||
export function ExportConfig():Promise<string>;
|
export function ExportConfig():Promise<string>;
|
||||||
|
|
||||||
export function Follow(arg1:string):Promise<string>;
|
export function Follow(arg1:string):Promise<string>;
|
||||||
@ -33,7 +39,7 @@ export function GetGroupList():Promise<Array<data.Group>>;
|
|||||||
|
|
||||||
export function GetGroupStockList(arg1:number):Promise<Array<data.GroupStock>>;
|
export function GetGroupStockList(arg1:number):Promise<Array<data.GroupStock>>;
|
||||||
|
|
||||||
export function GetIndustryMoneyRankSina(arg1:string):Promise<Array<Record<string, any>>>;
|
export function GetIndustryMoneyRankSina(arg1:string,arg2:string):Promise<Array<Record<string, any>>>;
|
||||||
|
|
||||||
export function GetIndustryRank(arg1:string,arg2:number):Promise<Array<any>>;
|
export function GetIndustryRank(arg1:string,arg2:number):Promise<Array<any>>;
|
||||||
|
|
||||||
@ -61,8 +67,22 @@ export function GlobalStockIndexes():Promise<Record<string, any>>;
|
|||||||
|
|
||||||
export function Greet(arg1:string):Promise<data.StockInfo>;
|
export function Greet(arg1:string):Promise<data.StockInfo>;
|
||||||
|
|
||||||
|
export function HotEvent(arg1:number):Promise<any>;
|
||||||
|
|
||||||
|
export function HotStock(arg1:string):Promise<any>;
|
||||||
|
|
||||||
|
export function HotTopic(arg1:number):Promise<Array<any>>;
|
||||||
|
|
||||||
|
export function IndustryResearchReport(arg1:string):Promise<Array<any>>;
|
||||||
|
|
||||||
|
export function InvestCalendarTimeLine(arg1:string):Promise<Array<any>>;
|
||||||
|
|
||||||
|
export function LongTigerRank(arg1:string):Promise<any>;
|
||||||
|
|
||||||
export function NewChatStream(arg1:string,arg2:string,arg3:string,arg4:any):Promise<void>;
|
export function NewChatStream(arg1:string,arg2:string,arg3:string,arg4:any):Promise<void>;
|
||||||
|
|
||||||
|
export function NewsPush(arg1:any):Promise<void>;
|
||||||
|
|
||||||
export function ReFleshTelegraphList(arg1:string):Promise<any>;
|
export function ReFleshTelegraphList(arg1:string):Promise<any>;
|
||||||
|
|
||||||
export function RemoveGroup(arg1:number):Promise<string>;
|
export function RemoveGroup(arg1:number):Promise<string>;
|
||||||
@ -73,6 +93,8 @@ export function SaveAIResponseResult(arg1:string,arg2:string,arg3:string,arg4:st
|
|||||||
|
|
||||||
export function SaveAsMarkdown(arg1:string,arg2:string):Promise<string>;
|
export function SaveAsMarkdown(arg1:string,arg2:string):Promise<string>;
|
||||||
|
|
||||||
|
export function SearchStock(arg1:string):Promise<Record<string, any>>;
|
||||||
|
|
||||||
export function SendDingDingMessage(arg1:string,arg2:string):Promise<string>;
|
export function SendDingDingMessage(arg1:string,arg2:string):Promise<string>;
|
||||||
|
|
||||||
export function SendDingDingMessageByType(arg1:string,arg2:string,arg3:number):Promise<string>;
|
export function SendDingDingMessageByType(arg1:string,arg2:string,arg3:number):Promise<string>;
|
||||||
@ -87,6 +109,10 @@ export function SetStockSort(arg1:number,arg2:string):Promise<void>;
|
|||||||
|
|
||||||
export function ShareAnalysis(arg1:string,arg2:string):Promise<string>;
|
export function ShareAnalysis(arg1:string,arg2:string):Promise<string>;
|
||||||
|
|
||||||
|
export function StockNotice(arg1:string):Promise<Array<any>>;
|
||||||
|
|
||||||
|
export function StockResearchReport(arg1:string):Promise<Array<any>>;
|
||||||
|
|
||||||
export function SummaryStockNews(arg1:string,arg2:any):Promise<void>;
|
export function SummaryStockNews(arg1:string,arg2:any):Promise<void>;
|
||||||
|
|
||||||
export function UnFollow(arg1:string):Promise<string>;
|
export function UnFollow(arg1:string):Promise<string>;
|
||||||
|
@ -18,14 +18,26 @@ export function AddStockGroup(arg1, arg2) {
|
|||||||
return window['go']['main']['App']['AddStockGroup'](arg1, arg2);
|
return window['go']['main']['App']['AddStockGroup'](arg1, arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function AnalyzeSentiment(arg1) {
|
||||||
|
return window['go']['main']['App']['AnalyzeSentiment'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function CheckUpdate() {
|
export function CheckUpdate() {
|
||||||
return window['go']['main']['App']['CheckUpdate']();
|
return window['go']['main']['App']['CheckUpdate']();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function ClsCalendar() {
|
||||||
|
return window['go']['main']['App']['ClsCalendar']();
|
||||||
|
}
|
||||||
|
|
||||||
export function DelPrompt(arg1) {
|
export function DelPrompt(arg1) {
|
||||||
return window['go']['main']['App']['DelPrompt'](arg1);
|
return window['go']['main']['App']['DelPrompt'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function EMDictCode(arg1) {
|
||||||
|
return window['go']['main']['App']['EMDictCode'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function ExportConfig() {
|
export function ExportConfig() {
|
||||||
return window['go']['main']['App']['ExportConfig']();
|
return window['go']['main']['App']['ExportConfig']();
|
||||||
}
|
}
|
||||||
@ -62,8 +74,8 @@ export function GetGroupStockList(arg1) {
|
|||||||
return window['go']['main']['App']['GetGroupStockList'](arg1);
|
return window['go']['main']['App']['GetGroupStockList'](arg1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GetIndustryMoneyRankSina(arg1) {
|
export function GetIndustryMoneyRankSina(arg1, arg2) {
|
||||||
return window['go']['main']['App']['GetIndustryMoneyRankSina'](arg1);
|
return window['go']['main']['App']['GetIndustryMoneyRankSina'](arg1, arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GetIndustryRank(arg1, arg2) {
|
export function GetIndustryRank(arg1, arg2) {
|
||||||
@ -118,10 +130,38 @@ export function Greet(arg1) {
|
|||||||
return window['go']['main']['App']['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);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InvestCalendarTimeLine(arg1) {
|
||||||
|
return window['go']['main']['App']['InvestCalendarTimeLine'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LongTigerRank(arg1) {
|
||||||
|
return window['go']['main']['App']['LongTigerRank'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function NewChatStream(arg1, arg2, arg3, arg4) {
|
export function NewChatStream(arg1, arg2, arg3, arg4) {
|
||||||
return window['go']['main']['App']['NewChatStream'](arg1, arg2, arg3, arg4);
|
return window['go']['main']['App']['NewChatStream'](arg1, arg2, arg3, arg4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function NewsPush(arg1) {
|
||||||
|
return window['go']['main']['App']['NewsPush'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function ReFleshTelegraphList(arg1) {
|
export function ReFleshTelegraphList(arg1) {
|
||||||
return window['go']['main']['App']['ReFleshTelegraphList'](arg1);
|
return window['go']['main']['App']['ReFleshTelegraphList'](arg1);
|
||||||
}
|
}
|
||||||
@ -142,6 +182,10 @@ export function SaveAsMarkdown(arg1, arg2) {
|
|||||||
return window['go']['main']['App']['SaveAsMarkdown'](arg1, arg2);
|
return window['go']['main']['App']['SaveAsMarkdown'](arg1, arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function SearchStock(arg1) {
|
||||||
|
return window['go']['main']['App']['SearchStock'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function SendDingDingMessage(arg1, arg2) {
|
export function SendDingDingMessage(arg1, arg2) {
|
||||||
return window['go']['main']['App']['SendDingDingMessage'](arg1, arg2);
|
return window['go']['main']['App']['SendDingDingMessage'](arg1, arg2);
|
||||||
}
|
}
|
||||||
@ -170,6 +214,14 @@ export function ShareAnalysis(arg1, arg2) {
|
|||||||
return window['go']['main']['App']['ShareAnalysis'](arg1, arg2);
|
return window['go']['main']['App']['ShareAnalysis'](arg1, arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function StockNotice(arg1) {
|
||||||
|
return window['go']['main']['App']['StockNotice'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function StockResearchReport(arg1) {
|
||||||
|
return window['go']['main']['App']['StockResearchReport'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
export function SummaryStockNews(arg1, arg2) {
|
export function SummaryStockNews(arg1, arg2) {
|
||||||
return window['go']['main']['App']['SummaryStockNews'](arg1, arg2);
|
return window['go']['main']['App']['SummaryStockNews'](arg1, arg2);
|
||||||
}
|
}
|
||||||
|
@ -290,6 +290,26 @@ export namespace data {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export class SentimentResult {
|
||||||
|
Score: number;
|
||||||
|
Category: number;
|
||||||
|
PositiveCount: number;
|
||||||
|
NegativeCount: number;
|
||||||
|
Description: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new SentimentResult(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.Score = source["Score"];
|
||||||
|
this.Category = source["Category"];
|
||||||
|
this.PositiveCount = source["PositiveCount"];
|
||||||
|
this.NegativeCount = source["NegativeCount"];
|
||||||
|
this.Description = source["Description"];
|
||||||
|
}
|
||||||
|
}
|
||||||
export class Settings {
|
export class Settings {
|
||||||
ID: number;
|
ID: number;
|
||||||
// Go type: time
|
// Go type: time
|
||||||
@ -322,6 +342,7 @@ export namespace data {
|
|||||||
darkTheme: boolean;
|
darkTheme: boolean;
|
||||||
browserPoolSize: number;
|
browserPoolSize: number;
|
||||||
enableFund: boolean;
|
enableFund: boolean;
|
||||||
|
enablePushNews: boolean;
|
||||||
|
|
||||||
static createFrom(source: any = {}) {
|
static createFrom(source: any = {}) {
|
||||||
return new Settings(source);
|
return new Settings(source);
|
||||||
@ -357,6 +378,7 @@ export namespace data {
|
|||||||
this.darkTheme = source["darkTheme"];
|
this.darkTheme = source["darkTheme"];
|
||||||
this.browserPoolSize = source["browserPoolSize"];
|
this.browserPoolSize = source["browserPoolSize"];
|
||||||
this.enableFund = source["enableFund"];
|
this.enableFund = source["enableFund"];
|
||||||
|
this.enablePushNews = source["enablePushNews"];
|
||||||
}
|
}
|
||||||
|
|
||||||
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||||
|
17
go.mod
17
go.mod
@ -1,8 +1,6 @@
|
|||||||
module go-stock
|
module go-stock
|
||||||
|
|
||||||
go 1.23
|
go 1.23.0
|
||||||
|
|
||||||
toolchain go1.23.0
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/PuerkitoBio/goquery v1.10.1
|
github.com/PuerkitoBio/goquery v1.10.1
|
||||||
@ -11,16 +9,18 @@ require (
|
|||||||
github.com/duke-git/lancet/v2 v2.3.4
|
github.com/duke-git/lancet/v2 v2.3.4
|
||||||
github.com/energye/systray v1.0.2
|
github.com/energye/systray v1.0.2
|
||||||
github.com/glebarez/sqlite v1.11.0
|
github.com/glebarez/sqlite v1.11.0
|
||||||
|
github.com/go-ego/gse v0.80.3
|
||||||
github.com/go-resty/resty/v2 v2.16.2
|
github.com/go-resty/resty/v2 v2.16.2
|
||||||
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4
|
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4
|
||||||
github.com/robertkrimen/otto v0.5.1
|
github.com/robertkrimen/otto v0.5.1
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/samber/lo v1.49.1
|
github.com/samber/lo v1.49.1
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
|
github.com/tidwall/gjson v1.14.2
|
||||||
github.com/wailsapp/wails/v2 v2.10.1
|
github.com/wailsapp/wails/v2 v2.10.1
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
golang.org/x/sys v0.30.0
|
golang.org/x/sys v0.31.0
|
||||||
golang.org/x/text v0.22.0
|
golang.org/x/text v0.23.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
gorm.io/gorm v1.25.12
|
gorm.io/gorm v1.25.12
|
||||||
gorm.io/plugin/dbresolver v1.5.3
|
gorm.io/plugin/dbresolver v1.5.3
|
||||||
@ -62,15 +62,18 @@ require (
|
|||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/tevino/abool v0.0.0-20220530134649-2bfc934cb23c // indirect
|
github.com/tevino/abool v0.0.0-20220530134649-2bfc934cb23c // indirect
|
||||||
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
github.com/tkrajina/go-reflector v0.5.8 // indirect
|
github.com/tkrajina/go-reflector v0.5.8 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||||
|
github.com/vcaesar/cedar v0.20.2 // indirect
|
||||||
github.com/wailsapp/go-webview2 v1.0.19 // indirect
|
github.com/wailsapp/go-webview2 v1.0.19 // indirect
|
||||||
github.com/wailsapp/mimetype v1.4.1 // indirect
|
github.com/wailsapp/mimetype v1.4.1 // indirect
|
||||||
go.uber.org/multierr v1.10.0 // indirect
|
go.uber.org/multierr v1.10.0 // indirect
|
||||||
golang.org/x/crypto v0.33.0 // indirect
|
golang.org/x/crypto v0.36.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||||
golang.org/x/net v0.35.0 // indirect
|
golang.org/x/net v0.38.0 // indirect
|
||||||
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
gopkg.in/sourcemap.v1 v1.0.5 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
modernc.org/libc v1.22.5 // indirect
|
modernc.org/libc v1.22.5 // indirect
|
||||||
|
28
go.sum
28
go.sum
@ -26,6 +26,8 @@ github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9g
|
|||||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
||||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
||||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
||||||
|
github.com/go-ego/gse v0.80.3 h1:YNFkjMhlhQnUeuoFcUEd1ivh6SOB764rT8GDsEbDiEg=
|
||||||
|
github.com/go-ego/gse v0.80.3/go.mod h1:Gt3A9Ry1Eso2Kza4MRaiZ7f2DTAvActmETY46Lxg0gU=
|
||||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||||
github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg=
|
github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg=
|
||||||
@ -117,12 +119,22 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
|
|||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/tevino/abool v0.0.0-20220530134649-2bfc934cb23c h1:coVla7zpsycc+kA9NXpcvv2E4I7+ii6L5hZO2S6C3kw=
|
github.com/tevino/abool v0.0.0-20220530134649-2bfc934cb23c h1:coVla7zpsycc+kA9NXpcvv2E4I7+ii6L5hZO2S6C3kw=
|
||||||
github.com/tevino/abool v0.0.0-20220530134649-2bfc934cb23c/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
|
github.com/tevino/abool v0.0.0-20220530134649-2bfc934cb23c/go.mod h1:qc66Pna1RiIsPa7O4Egxxs9OqkuxDX55zznh9K07Tzg=
|
||||||
|
github.com/tidwall/gjson v1.14.2 h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo=
|
||||||
|
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ=
|
github.com/tkrajina/go-reflector v0.5.8 h1:yPADHrwmUbMq4RGEyaOUpz2H90sRsETNVpjzo3DLVQQ=
|
||||||
github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
|
github.com/tkrajina/go-reflector v0.5.8/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||||
|
github.com/vcaesar/cedar v0.20.2 h1:TDx7AdZhilKcfE1WvdToTJf5VrC/FXcUOW+KY1upLZ4=
|
||||||
|
github.com/vcaesar/cedar v0.20.2/go.mod h1:lyuGvALuZZDPNXwpzv/9LyxW+8Y6faN7zauFezNsnik=
|
||||||
|
github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4=
|
||||||
|
github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU=
|
||||||
github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU=
|
github.com/wailsapp/go-webview2 v1.0.19 h1:7U3QcDj1PrBPaxJNCui2k1SkWml+Q5kvFUFyTImA6NU=
|
||||||
github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
|
github.com/wailsapp/go-webview2 v1.0.19/go.mod h1:qJmWAmAmaniuKGZPWwne+uor3AHMB5PFhqiK0Bbj8kc=
|
||||||
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
github.com/wailsapp/mimetype v1.4.1 h1:pQN9ycO7uo4vsUUuPeHEYoUkLVkaRntMnHJxVwYhwHs=
|
||||||
@ -142,8 +154,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
|||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
@ -161,8 +173,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
|||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -187,8 +199,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
@ -208,8 +220,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
71
main.go
71
main.go
@ -5,7 +5,7 @@ import (
|
|||||||
"embed"
|
"embed"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/duke-git/lancet/v2/convertor"
|
"github.com/duke-git/lancet/v2/slice"
|
||||||
"github.com/wailsapp/wails/v2"
|
"github.com/wailsapp/wails/v2"
|
||||||
"github.com/wailsapp/wails/v2/pkg/logger"
|
"github.com/wailsapp/wails/v2/pkg/logger"
|
||||||
"github.com/wailsapp/wails/v2/pkg/menu"
|
"github.com/wailsapp/wails/v2/pkg/menu"
|
||||||
@ -21,6 +21,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
goruntime "runtime"
|
goruntime "runtime"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -202,6 +203,7 @@ func AutoMigrate() {
|
|||||||
db.Dao.AutoMigrate(&models.Tags{})
|
db.Dao.AutoMigrate(&models.Tags{})
|
||||||
db.Dao.AutoMigrate(&models.Telegraph{})
|
db.Dao.AutoMigrate(&models.Telegraph{})
|
||||||
db.Dao.AutoMigrate(&models.TelegraphTags{})
|
db.Dao.AutoMigrate(&models.TelegraphTags{})
|
||||||
|
db.Dao.AutoMigrate(&models.LongTigerRankData{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func initStockDataUS(ctx context.Context) {
|
func initStockDataUS(ctx context.Context) {
|
||||||
@ -270,7 +272,7 @@ func initStockData(ctx context.Context) {
|
|||||||
defer func() {
|
defer func() {
|
||||||
go runtime.EventsEmit(ctx, "loadingMsg", "done")
|
go runtime.EventsEmit(ctx, "loadingMsg", "done")
|
||||||
}()
|
}()
|
||||||
|
fields := "ts_code,symbol,name,area,industry,cnspell,market,list_date,act_name,act_ent_type,fullname,exchange,list_status,curr_type,enname,delist_date,is_hs"
|
||||||
log.SugaredLogger.Info("init stock data")
|
log.SugaredLogger.Info("init stock data")
|
||||||
res := &data.TushareStockBasicResponse{}
|
res := &data.TushareStockBasicResponse{}
|
||||||
err := json.Unmarshal(stocksBin, res)
|
err := json.Unmarshal(stocksBin, res)
|
||||||
@ -278,26 +280,24 @@ func initStockData(ctx context.Context) {
|
|||||||
log.SugaredLogger.Error(err.Error())
|
log.SugaredLogger.Error(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range res.Data.Items {
|
for _, item := range res.Data.Items {
|
||||||
stock := &data.StockBasic{}
|
stock := &data.StockBasic{}
|
||||||
stock.Exchange = convertor.ToString(item[0])
|
stockData := map[string]any{}
|
||||||
stock.IsHs = convertor.ToString(item[1])
|
for _, field := range strings.Split(fields, ",") {
|
||||||
stock.Name = convertor.ToString(item[2])
|
//logger.SugaredLogger.Infof("field: %s", field)
|
||||||
stock.Industry = convertor.ToString(item[3])
|
idx := slice.IndexOf(res.Data.Fields, field)
|
||||||
stock.ListStatus = convertor.ToString(item[4])
|
if idx == -1 {
|
||||||
stock.ActName = convertor.ToString(item[5])
|
continue
|
||||||
stock.ID = uint(item[6].(float64))
|
}
|
||||||
stock.CurrType = convertor.ToString(item[7])
|
stockData[field] = item[idx]
|
||||||
stock.Area = convertor.ToString(item[8])
|
}
|
||||||
stock.ListDate = convertor.ToString(item[9])
|
jsonData, _ := json.Marshal(stockData)
|
||||||
stock.DelistDate = convertor.ToString(item[10])
|
err := json.Unmarshal(jsonData, stock)
|
||||||
stock.ActEntType = convertor.ToString(item[11])
|
if err != nil {
|
||||||
stock.TsCode = convertor.ToString(item[12])
|
continue
|
||||||
stock.Symbol = convertor.ToString(item[13])
|
}
|
||||||
stock.Cnspell = convertor.ToString(item[14])
|
stock.ID = 0
|
||||||
stock.Fullname = convertor.ToString(item[20])
|
|
||||||
stock.Ename = convertor.ToString(item[21])
|
|
||||||
|
|
||||||
var count int64
|
var count int64
|
||||||
db.Dao.Model(&data.StockBasic{}).Where("ts_code = ?", stock.TsCode).Count(&count)
|
db.Dao.Model(&data.StockBasic{}).Where("ts_code = ?", stock.TsCode).Count(&count)
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
@ -305,7 +305,38 @@ func initStockData(ctx context.Context) {
|
|||||||
} else {
|
} else {
|
||||||
db.Dao.Create(stock)
|
db.Dao.Create(stock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//db.Dao.Model(&data.StockBasic{}).FirstOrCreate(stock, &data.StockBasic{TsCode: stock.TsCode}).Where("ts_code = ?", stock.TsCode).Updates(stock)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//for _, item := range res.Data.Items {
|
||||||
|
// stock := &data.StockBasic{}
|
||||||
|
// stock.Exchange = convertor.ToString(item[0])
|
||||||
|
// stock.IsHs = convertor.ToString(item[1])
|
||||||
|
// stock.Name = convertor.ToString(item[2])
|
||||||
|
// stock.Industry = convertor.ToString(item[3])
|
||||||
|
// stock.ListStatus = convertor.ToString(item[4])
|
||||||
|
// stock.ActName = convertor.ToString(item[5])
|
||||||
|
// stock.ID = uint(item[6].(float64))
|
||||||
|
// stock.CurrType = convertor.ToString(item[7])
|
||||||
|
// stock.Area = convertor.ToString(item[8])
|
||||||
|
// stock.ListDate = convertor.ToString(item[9])
|
||||||
|
// stock.DelistDate = convertor.ToString(item[10])
|
||||||
|
// stock.ActEntType = convertor.ToString(item[11])
|
||||||
|
// stock.TsCode = convertor.ToString(item[12])
|
||||||
|
// stock.Symbol = convertor.ToString(item[13])
|
||||||
|
// stock.Cnspell = convertor.ToString(item[14])
|
||||||
|
// stock.Fullname = convertor.ToString(item[20])
|
||||||
|
// stock.Ename = convertor.ToString(item[21])
|
||||||
|
//
|
||||||
|
// var count int64
|
||||||
|
// db.Dao.Model(&data.StockBasic{}).Where("ts_code = ?", stock.TsCode).Count(&count)
|
||||||
|
// if count > 0 {
|
||||||
|
// continue
|
||||||
|
// } else {
|
||||||
|
// db.Dao.Create(stock)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDir(dir string) {
|
func checkDir(dir string) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user