Compare commits

...

6 Commits

Author SHA1 Message Date
ArvinLovegood
490a3c0847 feat(app):增加恒生科技指数并优化版本更新提示信息
- 在市场组件中添加恒生科技指数选项
- 更新版本时增加提交信息显示
- 优化新版本下载失败提示信息
2025-07-14 11:34:01 +08:00
ArvinLovegood
38f83674ef feat(data):添加PPI和PMI
- 新增 GetPPI 和 GetPMI 函数,用于获取工业品出厂价格指数和采购经理人指数数据
- 添加相关测试用例,验证 PPI 和 PMI接口的功能
- 更新模型结构,支持 PPI 和 PMI 数据
- 在 OpenAI API 中调用新增的 PPI 和 PMI 接口,丰富市场数据信息
2025-07-12 13:31:38 +08:00
ArvinLovegood
d26c4bc986 feat(data):AI市场资讯总结添加国内宏观经济数据(GDP和CPI,后期陆续会加其他数据)
- 在 openai_api.go 中添加 GDP 和 CPI 数据的获取和格式化输出
- 在 market_news_api_test.go 中更新相关测试函数
- 在 struct_to_markdown.go 中新增 MarkdownTableWithTitle 函数用于添加标题
2025-07-11 18:58:31 +08:00
ArvinLovegood
7e919376b5 feat(data):添加GDP和CPI数据接口
- 实现了 GetGDP 和 GetCPI 方法,获取国内生产总值和居民消费价格指数数据
- 新增 GDP 和 CPI 数据模型
- 更新相关测试用例
2025-07-11 18:39:24 +08:00
ArvinLovegood
1d9ef724e6 feat(frontend):重命名"指标行情"标签为"重大指数"
- 重命名"指标行情"标签为"重大指数"
- 新增多个重大指数的行情图表,包括:
  - 科创芯片(sh000685)
  - 证券龙头(sz399437)
  - 高端装备(sz399437)  - 中证银行(sz399986)
  - 上证医药(sh000037)
- 统一设置为暗黑主题
2025-07-11 18:05:45 +08:00
ArvinLovegood
8e982d4430 refactor(main):注释掉隐藏到托盘区的功能
-移除了对 runtime 包的导入
- 注释掉了相关代码块
2025-07-11 17:53:55 +08:00
9 changed files with 293 additions and 20 deletions

6
app.go
View File

@@ -258,7 +258,7 @@ func (a *App) CheckUpdate() {
"time": "发现新版本:" + releaseVersion.TagName,
"isRed": false,
"source": "go-stock",
"content": fmt.Sprintf("当前版本:%s, 最新版本:%s,后台开始下载...", Version, releaseVersion.TagName),
"content": fmt.Sprintf("当前版本:%s, 最新版本:%s,后台开始下载...\n%s", Version, releaseVersion.TagName, commit.Message),
})
resp, err := resty.New().R().Get(downloadUrl)
if err != nil {
@@ -266,7 +266,7 @@ func (a *App) CheckUpdate() {
"time": "新版本:" + releaseVersion.TagName,
"isRed": true,
"source": "go-stock",
"content": "新版本下载失败,请稍后重试或请前往 https://github.com/ArvinLovegood/go-stock/releases 手动下载替换文件。",
"content": commit.Message + "\n新版本下载失败,请稍后重试或请前往 https://github.com/ArvinLovegood/go-stock/releases 手动下载替换文件。",
})
return
}
@@ -277,7 +277,7 @@ func (a *App) CheckUpdate() {
"time": "新版本:" + releaseVersion.TagName,
"isRed": true,
"source": "go-stock",
"content": "新版本下载失败,请稍后重试或请前往 https://github.com/ArvinLovegood/go-stock/releases 手动下载替换文件。",
"content": commit.Message + "\n新版本下载失败,请稍后重试或请前往 https://github.com/ArvinLovegood/go-stock/releases 手动下载替换文件。",
})
return
}

View File

@@ -702,7 +702,9 @@ func (m MarketNewsApi) ClsCalendar() []any {
return respMap["data"].([]any)
}
func (m MarketNewsApi) GetGDP() {
func (m MarketNewsApi) GetGDP() *models.GDPResp {
res := &models.GDPResp{}
url := "https://datacenter-web.eastmoney.com/api/data/v1/get?callback=data&columns=REPORT_DATE%2CTIME%2CDOMESTICL_PRODUCT_BASE%2CFIRST_PRODUCT_BASE%2CSECOND_PRODUCT_BASE%2CTHIRD_PRODUCT_BASE%2CSUM_SAME%2CFIRST_SAME%2CSECOND_SAME%2CTHIRD_SAME&pageNumber=1&pageSize=20&sortColumns=REPORT_DATE&sortTypes=-1&source=WEB&client=WEB&reportName=RPT_ECONOMY_GDP&p=1&pageNo=1&pageNum=1&_=" + strconv.FormatInt(time.Now().Unix(), 10)
resp, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R().
SetHeader("Host", "datacenter-web.eastmoney.com").
@@ -712,7 +714,7 @@ func (m MarketNewsApi) GetGDP() {
Get(url)
if err != nil {
logger.SugaredLogger.Errorf("GDP err:%s", err.Error())
return
return res
}
body := resp.Body()
logger.SugaredLogger.Debugf("GDP:%s", body)
@@ -722,13 +724,111 @@ func (m MarketNewsApi) GetGDP() {
val, err := vm.Run(body)
if err != nil {
logger.SugaredLogger.Errorf("GDP err:%s", err.Error())
return
return res
}
data, _ := val.Object().Value().Export()
logger.SugaredLogger.Infof("GDP:%v", data)
marshal, err := json.Marshal(data)
if err != nil {
return
return res
}
logger.SugaredLogger.Infof("GDP:%s", marshal)
json.Unmarshal(marshal, &res)
logger.SugaredLogger.Infof("GDP:%+v", res)
return res
}
func (m MarketNewsApi) GetCPI() *models.CPIResp {
res := &models.CPIResp{}
url := "https://datacenter-web.eastmoney.com/api/data/v1/get?callback=data&columns=REPORT_DATE%2CTIME%2CNATIONAL_SAME%2CNATIONAL_BASE%2CNATIONAL_SEQUENTIAL%2CNATIONAL_ACCUMULATE%2CCITY_SAME%2CCITY_BASE%2CCITY_SEQUENTIAL%2CCITY_ACCUMULATE%2CRURAL_SAME%2CRURAL_BASE%2CRURAL_SEQUENTIAL%2CRURAL_ACCUMULATE&pageNumber=1&pageSize=20&sortColumns=REPORT_DATE&sortTypes=-1&source=WEB&client=WEB&reportName=RPT_ECONOMY_CPI&p=1&pageNo=1&pageNum=1&_=" + strconv.FormatInt(time.Now().Unix(), 10)
resp, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R().
SetHeader("Host", "datacenter-web.eastmoney.com").
SetHeader("Origin", "https://datacenter.eastmoney.com").
SetHeader("Referer", "https://data.eastmoney.com/cjsj/gdp.html").
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("GDP err:%s", err.Error())
return res
}
body := resp.Body()
logger.SugaredLogger.Debugf("GDP:%s", body)
vm := otto.New()
vm.Run("function data(res){return res};")
val, err := vm.Run(body)
if err != nil {
logger.SugaredLogger.Errorf("GDP err:%s", err.Error())
return res
}
data, _ := val.Object().Value().Export()
logger.SugaredLogger.Infof("GDP:%v", data)
marshal, err := json.Marshal(data)
if err != nil {
return res
}
json.Unmarshal(marshal, &res)
logger.SugaredLogger.Infof("GDP:%+v", res)
return res
}
// GetPPI PPI
func (m MarketNewsApi) GetPPI() *models.PPIResp {
res := &models.PPIResp{}
url := "https://datacenter-web.eastmoney.com/api/data/v1/get?callback=data&columns=REPORT_DATE,TIME,BASE,BASE_SAME,BASE_ACCUMULATE&pageNumber=1&pageSize=20&sortColumns=REPORT_DATE&sortTypes=-1&source=WEB&client=WEB&reportName=RPT_ECONOMY_PPI&p=1&pageNo=1&pageNum=1&_=" + strconv.FormatInt(time.Now().Unix(), 10)
resp, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R().
SetHeader("Host", "datacenter-web.eastmoney.com").
SetHeader("Origin", "https://datacenter.eastmoney.com").
SetHeader("Referer", "https://data.eastmoney.com/cjsj/gdp.html").
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("GDP err:%s", err.Error())
return res
}
body := resp.Body()
vm := otto.New()
vm.Run("function data(res){return res};")
val, err := vm.Run(body)
if err != nil {
return res
}
data, _ := val.Object().Value().Export()
marshal, err := json.Marshal(data)
if err != nil {
return res
}
json.Unmarshal(marshal, &res)
return res
}
func (m MarketNewsApi) GetPMI() *models.PMIResp {
res := &models.PMIResp{}
url := "https://datacenter-web.eastmoney.com/api/data/v1/get?callback=data&columns=REPORT_DATE%2CTIME%2CMAKE_INDEX%2CMAKE_SAME%2CNMAKE_INDEX%2CNMAKE_SAME&pageNumber=1&pageSize=20&sortColumns=REPORT_DATE&sortTypes=-1&source=WEB&client=WEB&reportName=RPT_ECONOMY_PMI&p=1&pageNo=1&pageNum=1&_=" + strconv.FormatInt(time.Now().Unix(), 10)
resp, err := resty.New().SetTimeout(time.Duration(30)*time.Second).R().
SetHeader("Host", "datacenter-web.eastmoney.com").
SetHeader("Origin", "https://datacenter.eastmoney.com").
SetHeader("Referer", "https://data.eastmoney.com/cjsj/gdp.html").
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 {
return res
}
body := resp.Body()
vm := otto.New()
vm.Run("function data(res){return res};")
val, err := vm.Run(body)
if err != nil {
return res
}
data, _ := val.Object().Value().Export()
marshal, err := json.Marshal(data)
if err != nil {
return res
}
json.Unmarshal(marshal, &res)
return res
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/tidwall/gjson"
"go-stock/backend/db"
"go-stock/backend/logger"
"go-stock/backend/util"
"strings"
"testing"
)
@@ -177,5 +178,26 @@ func TestClsCalendar(t *testing.T) {
}
func TestGetGDP(t *testing.T) {
NewMarketNewsApi().GetGDP()
res := NewMarketNewsApi().GetGDP()
md := util.MarkdownTableWithTitle("国内生产总值(GDP)", res.GDPResult.Data)
logger.SugaredLogger.Debugf(md)
}
func TestGetCPI(t *testing.T) {
res := NewMarketNewsApi().GetCPI()
md := util.MarkdownTableWithTitle("居民消费价格指数(CPI)", res.CPIResult.Data)
logger.SugaredLogger.Debugf(md)
}
// PPI
func TestGetPPI(t *testing.T) {
res := NewMarketNewsApi().GetPPI()
md := util.MarkdownTableWithTitle("工业品出厂价格指数(PPI)", res.PPIResult.Data)
logger.SugaredLogger.Debugf(md)
}
// PMI
func TestGetPMI(t *testing.T) {
res := NewMarketNewsApi().GetPMI()
md := util.MarkdownTableWithTitle("采购经理人指数(PMI)", res.PMIResult.Data)
logger.SugaredLogger.Debugf(md)
}

View File

@@ -17,6 +17,7 @@ import (
"go-stock/backend/db"
"go-stock/backend/logger"
"go-stock/backend/models"
"go-stock/backend/util"
"strings"
"sync"
"time"
@@ -178,7 +179,34 @@ func (o OpenAi) NewSummaryStockNewsStreamWithTools(userQuestion string, sysPromp
"content": "当前本地时间是:" + time.Now().Format("2006-01-02 15:04:05"),
})
wg := &sync.WaitGroup{}
wg.Add(2)
wg.Add(3)
go func() {
defer wg.Done()
var market strings.Builder
res := NewMarketNewsApi().GetGDP()
md := util.MarkdownTableWithTitle("国内生产总值(GDP)", res.GDPResult.Data)
market.WriteString(md)
res2 := NewMarketNewsApi().GetCPI()
md2 := util.MarkdownTableWithTitle("居民消费价格指数(CPI)", res2.CPIResult.Data)
market.WriteString(md2)
res3 := NewMarketNewsApi().GetPPI()
md3 := util.MarkdownTableWithTitle("工业品出厂价格指数(PPI)", res3.PPIResult.Data)
market.WriteString(md3)
res4 := NewMarketNewsApi().GetPMI()
md4 := util.MarkdownTableWithTitle("采购经理人指数(PMI)", res4.PMIResult.Data)
market.WriteString(md4)
msg = append(msg, map[string]interface{}{
"role": "user",
"content": "国内宏观经济数据",
})
msg = append(msg, map[string]interface{}{
"role": "assistant",
"content": "\n# 国内宏观经济数据:\n" + market.String(),
})
}()
go func() {
defer wg.Done()
var market strings.Builder

View File

@@ -383,3 +383,81 @@ type GDP struct {
THIRDPRODUCTBASE float64 `json:"THIRD_PRODUCT_BASE" md:"第三产业(亿元)"`
THIRDSAME float64 `json:"THIRD_SAME" md:"第三产业同比增长(%)"`
}
type CPI struct {
REPORTDATE string `json:"REPORT_DATE" md:"报告时间"`
TIME string `json:"TIME" md:"报告期"`
NATIONALBASE float64 `json:"NATIONAL_BASE" md:"全国当月"`
NATIONALSAME float64 `json:"NATIONAL_SAME" md:"全国当月同比增长(%)"`
NATIONALSEQUENTIAL float64 `json:"NATIONAL_SEQUENTIAL" md:"全国当月环比增长(%)"`
NATIONALACCUMULATE float64 `json:"NATIONAL_ACCUMULATE" md:"全国当月累计"`
CITYBASE float64 `json:"CITY_BASE" md:"城市当月"`
CITYSAME float64 `json:"CITY_SAME" md:"城市当月同比增长(%)"`
CITYSEQUENTIAL float64 `json:"CITY_SEQUENTIAL" md:"城市当月环比增长(%)"`
CITYACCUMULATE int `json:"CITY_ACCUMULATE" md:"城市当月累计"`
RURALBASE float64 `json:"RURAL_BASE" md:"农村当月"`
RURALSAME float64 `json:"RURAL_SAME" md:"农村当月同比增长(%)"`
RURALSEQUENTIAL int `json:"RURAL_SEQUENTIAL" md:"农村当月环比增长(%)"`
RURALACCUMULATE float64 `json:"RURAL_ACCUMULATE" md:"农村当月累计"`
}
type PPI struct {
REPORTDATE string `json:"REPORT_DATE" md:"报告时间"`
TIME string `json:"TIME" md:"报告期"`
BASE float64 `json:"BASE" md:"当月"`
BASESAME float64 `json:"BASE_SAME" md:"当月同比增长(%)"`
BASEACCUMULATE float64 `json:"BASE_ACCUMULATE" md:"累计"`
}
type PMI struct {
REPORTDATE string `md:"报告时间" json:"REPORT_DATE"`
TIME string `md:"报告期" json:"TIME"`
MAKEINDEX float64 `md:"制造业指数" json:"MAKE_INDEX"`
MAKESAME float64 `md:"制造业指数同比增长(%)" json:"MAKE_SAME"`
NMAKEINDEX float64 `md:"非制造业" json:"NMAKE_INDEX"`
NMAKESAME float64 `md:"非制造业同比增长(%)" json:"NMAKE_SAME"`
}
type DCResp struct {
Version string `json:"version"`
Success bool `json:"success"`
Message string `json:"message"`
Code int `json:"code"`
}
type GDPResult struct {
Pages int `json:"pages"`
Data []GDP `json:"data"`
Count int `json:"count"`
}
type CPIResult struct {
Pages int `json:"pages"`
Data []CPI `json:"data"`
Count int `json:"count"`
}
type PPIResult struct {
Pages int `json:"pages"`
Data []PPI `json:"data"`
Count int `json:"count"`
}
type PMIResult struct {
Pages int `json:"pages"`
Data []PMI `json:"data"`
Count int `json:"count"`
}
type GDPResp struct {
DCResp
GDPResult GDPResult `json:"result"`
}
type CPIResp struct {
DCResp
CPIResult CPIResult `json:"result"`
}
type PPIResp struct {
DCResp
PPIResult PPIResult `json:"result"`
}
type PMIResp struct {
DCResp
PMIResult PMIResult `json:"result"`
}

View File

@@ -29,6 +29,28 @@ func MarkdownTable(v interface{}) string {
return "输入必须是结构体、结构体指针、结构体切片或数组"
}
func MarkdownTableWithTitle(title string, v interface{}) string {
value := reflect.ValueOf(v)
if value.Kind() == reflect.Ptr {
value = value.Elem()
}
// 处理单个结构体
if value.Kind() == reflect.Struct {
return markdownSingleStruct(value)
}
// 处理结构体切片/数组
if value.Kind() == reflect.Slice || value.Kind() == reflect.Array {
if value.Len() == 0 {
return "\n## " + title + "\n" + "无数据" + "\n"
}
return "\n## " + title + "\n" + markdownStructSlice(value) + "\n"
}
return "\n## " + title + "\n" + "无数据" + "\n"
}
// 处理单个结构体
func markdownSingleStruct(value reflect.Value) string {
t := value.Type()

View File

@@ -180,15 +180,15 @@ const menuOptions = ref([
to: {
name: 'market',
query: {
name: "指标行情",
name: "重大指数",
}
},
onClick: () => {
activeKey.value = 'market'
EventsEmit("changeMarketTab", {ID: 0, name: '指标行情'})
EventsEmit("changeMarketTab", {ID: 0, name: '重大指数'})
},
},
{default: () => '指标行情',}
{default: () => '重大指数',}
),
key: 'market3',
icon: renderIcon(AnalyticsOutline),

View File

@@ -388,12 +388,36 @@ function ReFlesh(source) {
</n-tab-pane>
</n-tabs>
</n-tab-pane>
<n-tab-pane name="指标行情" tab="指标行情">
<n-tab-pane name="重大指数" tab="重大指数">
<n-tabs type="segment" animated>
<n-tab-pane name="恒生科技指数" tab="恒生科技指数">
<k-line-chart code="hkHSTECH" :chart-height="panelHeight" name="恒生科技指数" :k-days="20"
:dark-theme="true"></k-line-chart>
</n-tab-pane>
<n-tab-pane name="科创50" tab="科创50" >
<k-line-chart code="sh000688" :chart-height="panelHeight" name="科创50" :k-days="20"
:dark-theme="true"></k-line-chart>
</n-tab-pane>
<n-tab-pane name="科创芯片" tab="科创芯片" >
<k-line-chart code="sh000685" :chart-height="panelHeight" name="科创芯片" :k-days="20"
:dark-theme="true"></k-line-chart>
</n-tab-pane>
<n-tab-pane name="证券龙头" tab="证券龙头" >
<k-line-chart code="sz399437" :chart-height="panelHeight" name="证券龙头" :k-days="20"
:dark-theme="true"></k-line-chart>
</n-tab-pane>
<n-tab-pane name="高端装备" tab="高端装备" >
<k-line-chart code="sz399437" :chart-height="panelHeight" name="高端装备" :k-days="20"
:dark-theme="true"></k-line-chart>
</n-tab-pane>
<n-tab-pane name="中证银行" tab="中证银行">
<k-line-chart code="sz399986" :chart-height="panelHeight" name="中证银行" :k-days="20"
:dark-theme="true"></k-line-chart>
</n-tab-pane>
<n-tab-pane name="上证医药" tab="上证医药">
<k-line-chart code="sh000037" :chart-height="panelHeight" name="上证医药" :k-days="20"
:dark-theme="true"></k-line-chart>
</n-tab-pane>
<n-tab-pane name="沪深300" tab="沪深300">
<k-line-chart code="sh000300" :chart-height="panelHeight" name="沪深300" :k-days="20"
:dark-theme="true"></k-line-chart>

11
main.go
View File

@@ -19,7 +19,6 @@ import (
log "go-stock/backend/logger"
"go-stock/backend/models"
"os"
goruntime "runtime"
"runtime/debug"
"strings"
)
@@ -91,11 +90,11 @@ func main() {
//})
//FileMenu.AddSeparator()
if goruntime.GOOS == "windows" {
FileMenu.AddText("隐藏到托盘区", keys.CmdOrCtrl("z"), func(_ *menu.CallbackData) {
runtime.WindowHide(app.ctx)
})
}
//if goruntime.GOOS == "windows" {
// FileMenu.AddText("隐藏到托盘区", keys.CmdOrCtrl("z"), func(_ *menu.CallbackData) {
// runtime.WindowHide(app.ctx)
// })
//}
//FileMenu.AddText("退出", keys.CmdOrCtrl("q"), func(_ *menu.CallbackData) {
// runtime.Quit(app.ctx)