Compare commits
12 Commits
v2025.7.7.
...
v2025.7.8.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2f260c613 | ||
|
|
2d224ccfc4 | ||
|
|
a66f2156f1 | ||
|
|
e90727773f | ||
|
|
89dcb713be | ||
|
|
6f4b21207d | ||
|
|
f51e3d863a | ||
|
|
c180c2a5f8 | ||
|
|
3ba18e8ef2 | ||
|
|
f0314187e5 | ||
|
|
6440885688 | ||
|
|
2dd4f072b2 |
@@ -57,6 +57,9 @@
|
||||
| 不再强制依赖Chrome浏览器 | ✅ | 默认使用edge浏览器抓取新闻资讯 |
|
||||
|
||||
## 👀 更新日志
|
||||
### 2025.07.08 实现软件自动更新功能
|
||||
### 2025.07.07 卡片添加迷你分时图
|
||||
### 2025.07.05 MacOs支持
|
||||
### 2025.07.01 AI分析集成工具函数,AI分析将更加智能
|
||||
### 2025.06.30 添加指标选股功能
|
||||
### 2025.06.27 添加财经日历和重大事件时间轴功能
|
||||
|
||||
51
app.go
51
app.go
@@ -1,9 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/inconshreveable/go-update"
|
||||
"go-stock/backend/data"
|
||||
"go-stock/backend/db"
|
||||
"go-stock/backend/logger"
|
||||
@@ -111,6 +113,13 @@ func (a *App) CheckUpdate() {
|
||||
}
|
||||
logger.SugaredLogger.Infof("releaseVersion:%+v", releaseVersion.TagName)
|
||||
if releaseVersion.TagName != Version {
|
||||
go runtime.EventsEmit(a.ctx, "newsPush", map[string]any{
|
||||
"time": "发现新版本:" + releaseVersion.TagName,
|
||||
"isRed": false,
|
||||
"source": "go-stock",
|
||||
"content": fmt.Sprintf("当前版本:%s, 最新版本:%s,开始下载...", Version, releaseVersion.TagName),
|
||||
})
|
||||
|
||||
tag := &models.Tag{}
|
||||
_, err = resty.New().R().
|
||||
SetResult(tag).
|
||||
@@ -118,6 +127,7 @@ func (a *App) CheckUpdate() {
|
||||
if err == nil {
|
||||
releaseVersion.Tag = *tag
|
||||
}
|
||||
|
||||
commit := &models.Commit{}
|
||||
_, err = resty.New().R().
|
||||
SetResult(commit).
|
||||
@@ -126,7 +136,46 @@ func (a *App) CheckUpdate() {
|
||||
releaseVersion.Commit = *commit
|
||||
}
|
||||
|
||||
go runtime.EventsEmit(a.ctx, "updateVersion", releaseVersion)
|
||||
if !IsWindows() {
|
||||
go runtime.EventsEmit(a.ctx, "updateVersion", releaseVersion)
|
||||
return
|
||||
}
|
||||
|
||||
downloadUrl := fmt.Sprintf("https://github.com/ArvinLovegood/go-stock/releases/download/%s/go-stock-windows-amd64.exe", releaseVersion.TagName)
|
||||
resp, err := resty.New().R().Get(downloadUrl)
|
||||
if err != nil {
|
||||
go runtime.EventsEmit(a.ctx, "newsPush", map[string]any{
|
||||
"time": "新版本:" + releaseVersion.TagName,
|
||||
"isRed": true,
|
||||
"source": "go-stock",
|
||||
"content": "新版本下载失败,请前往 https://github.com/ArvinLovegood/go-stock/releases 手动下载替换文件。",
|
||||
})
|
||||
return
|
||||
}
|
||||
body := resp.Body()
|
||||
// 验证下载文件的哈希值
|
||||
//hash := sha256.Sum256(body)
|
||||
//actualSHA256 := hex.EncodeToString(hash[:])
|
||||
//logger.SugaredLogger.Infof("actualSHA256: %s", actualSHA256)
|
||||
//if actualSHA256 != releaseVersion.Commit.Sha {
|
||||
// logger.SugaredLogger.Errorf("下载文件sha256校验失败")
|
||||
// logger.SugaredLogger.Errorf("actualSHA256: %s Commit-Sha:%s", actualSHA256, releaseVersion.Commit.Sha)
|
||||
// return
|
||||
//}
|
||||
// 使用go-update库进行更新
|
||||
err = update.Apply(bytes.NewReader(body), update.Options{})
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error("更新失败: ", err.Error())
|
||||
go runtime.EventsEmit(a.ctx, "updateVersion", releaseVersion)
|
||||
return
|
||||
} else {
|
||||
go runtime.EventsEmit(a.ctx, "newsPush", map[string]any{
|
||||
"time": "新版本:" + releaseVersion.TagName,
|
||||
"isRed": true,
|
||||
"source": "go-stock",
|
||||
"content": "版本更新完成,下次重启软件生效.",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,11 @@ type OpenAi struct {
|
||||
BrowserPath string `json:"browser_path"`
|
||||
}
|
||||
|
||||
func (o OpenAi) String() string {
|
||||
return fmt.Sprintf("OpenAi{BaseUrl: %s, Model: %s, MaxTokens: %d, Temperature: %.2f, Prompt: %s, TimeOut: %d, QuestionTemplate: %s, CrawlTimeOut: %d, KDays: %d, BrowserPath: %s, ApiKey: [MASKED]}",
|
||||
o.BaseUrl, o.Model, o.MaxTokens, o.Temperature, o.Prompt, o.TimeOut, o.QuestionTemplate, o.CrawlTimeOut, o.KDays, o.BrowserPath)
|
||||
}
|
||||
|
||||
func NewDeepSeekOpenAi(ctx context.Context) *OpenAi {
|
||||
config := GetConfig()
|
||||
if config.OpenAiEnable {
|
||||
@@ -140,8 +145,8 @@ func (o OpenAi) NewSummaryStockNewsStreamWithTools(userQuestion string, sysPromp
|
||||
go func() {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger.SugaredLogger.Errorf("NewSummaryStockNewsStream goroutine panic :%s", err)
|
||||
logger.SugaredLogger.Errorf("NewSummaryStockNewsStream goroutine panic config:%v", o)
|
||||
logger.SugaredLogger.Errorf("NewSummaryStockNewsStream goroutine panic: %s", err)
|
||||
logger.SugaredLogger.Errorf("NewSummaryStockNewsStream goroutine panic config: %s", o.String())
|
||||
}
|
||||
}()
|
||||
defer close(ch)
|
||||
@@ -264,7 +269,7 @@ func (o OpenAi) NewSummaryStockNewsStream(userQuestion string, sysPromptId *int)
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logger.SugaredLogger.Errorf("NewSummaryStockNewsStream goroutine panic :%s", err)
|
||||
logger.SugaredLogger.Errorf("NewSummaryStockNewsStream goroutine panic config:%v", o)
|
||||
logger.SugaredLogger.Errorf("NewSummaryStockNewsStream goroutine panic config:%s", o.String())
|
||||
}
|
||||
}()
|
||||
defer close(ch)
|
||||
@@ -356,7 +361,7 @@ func (o OpenAi) NewChatStream(stock, stockCode, userQuestion string, sysPromptId
|
||||
if err := recover(); err != nil {
|
||||
logger.SugaredLogger.Errorf("NewChatStream goroutine panic :%s", err)
|
||||
logger.SugaredLogger.Errorf("NewChatStream goroutine panic stock:%s stockCode:%s", stock, stockCode)
|
||||
logger.SugaredLogger.Errorf("NewChatStream goroutine panic config:%v", o)
|
||||
logger.SugaredLogger.Errorf("NewChatStream goroutine panic config:%s", o.String())
|
||||
}
|
||||
}()
|
||||
defer close(ch)
|
||||
|
||||
@@ -80,7 +80,7 @@ EventsOn("updateVersion",async (msg) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-space vertical size="large">
|
||||
<n-space vertical size="large" style="--wails-draggable:drag">
|
||||
<!-- 软件描述 -->
|
||||
<n-card size="large">
|
||||
<n-divider title-placement="center">关于软件</n-divider>
|
||||
|
||||
@@ -199,6 +199,7 @@ function getAiSummary() {
|
||||
summaryModal.value = true
|
||||
loading.value = true
|
||||
GetAIResponseResult("市场资讯").then(result => {
|
||||
loading.value = false
|
||||
if (result.content) {
|
||||
aiSummary.value = result.content
|
||||
question.value = result.question
|
||||
@@ -310,7 +311,7 @@ function ReFlesh(source) {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-card>
|
||||
<n-card style="--wails-draggable:drag">
|
||||
<n-tabs type="line" animated @update-value="updateTab" :value="nowTab">
|
||||
<n-tab-pane name="市场快讯" tab="市场快讯">
|
||||
<n-grid :cols="2" :y-gap="0">
|
||||
|
||||
@@ -267,7 +267,7 @@ function deletePrompt(ID){
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-flex justify="left" style="text-align: left">
|
||||
<n-flex justify="left" style="text-align: left;--wails-draggable:drag" >
|
||||
<n-form ref="formRef" :label-placement="'left'" :label-align="'left'" >
|
||||
<n-card :title="()=> h(NTag, { type: 'primary',bordered:false },()=> '基础设置')" size="small" >
|
||||
<n-grid :cols="24" :x-gap="24" style="text-align: left" >
|
||||
|
||||
@@ -64,6 +64,7 @@ import {keys, padStart} from "lodash";
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import MoneyTrend from "./moneyTrend.vue";
|
||||
import {TaskTools} from "@vicons/carbon";
|
||||
import StockSparkLine from "./stockSparkLine.vue";
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -1796,8 +1797,8 @@ function searchStockReport(stockCode) {
|
||||
<n-card :data-sort="result.sort" :id="result['股票代码']" :data-code="result['股票代码']" :bordered="true"
|
||||
:title="result['股票名称']" :closable="false"
|
||||
@close="removeMonitor(result['股票代码'],result['股票名称'],result.key)">
|
||||
<n-grid :cols="1" :y-gap="6">
|
||||
<n-gi>
|
||||
<n-grid :cols="12" :y-gap="6">
|
||||
<n-gi :span="6">
|
||||
<n-text :type="result.type">
|
||||
<n-number-animation :duration="1000" :precision="2" :from="result['上次当前价格']"
|
||||
:to="Number(result['当前价格'])"/>
|
||||
@@ -1813,6 +1814,9 @@ function searchStockReport(stockCode) {
|
||||
<n-number-animation :duration="1000" :precision="2" :from="0" :to="result.profitAmountToday"/>
|
||||
</n-text>
|
||||
</n-gi>
|
||||
<n-gi :span="6">
|
||||
<stock-spark-line :last-price="Number(result['当前价格'])" :open-price="Number(result['昨日收盘价'])" :stock-code="result['股票代码']" :stock-name="result['股票名称']" ></stock-spark-line>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-grid :cols="2" :y-gap="4" :x-gap="4">
|
||||
<n-gi>
|
||||
|
||||
137
frontend/src/components/stockSparkLine.vue
Normal file
137
frontend/src/components/stockSparkLine.vue
Normal file
@@ -0,0 +1,137 @@
|
||||
<script setup>
|
||||
import {onMounted, onBeforeMount, ref, watchEffect} from "vue";
|
||||
import * as echarts from 'echarts';
|
||||
import {GetStockMinutePriceLineData} from "../../wailsjs/go/main/App"; // 如果您使用多个组件,请将此样式导入放在您的主文件中
|
||||
const {stockCode,stockName,lastPrice,openPrice,darkTheme} = defineProps({
|
||||
stockCode: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
stockName: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
lastPrice: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
openPrice: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
darkTheme: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
})
|
||||
|
||||
const chartRef=ref();
|
||||
|
||||
function setChartData(chart) {
|
||||
//console.log("setChartData")
|
||||
GetStockMinutePriceLineData(stockCode, stockName).then(result => {
|
||||
//console.log("GetStockMinutePriceLineData",result)
|
||||
const priceData = result.priceData
|
||||
let category = []
|
||||
let price = []
|
||||
let min = 0
|
||||
let max = 0
|
||||
for (let i = 0; i < priceData.length; i++) {
|
||||
category.push(priceData[i].time)
|
||||
price.push(priceData[i].price)
|
||||
if (min === 0 || min > priceData[i].price) {
|
||||
min = priceData[i].price
|
||||
}
|
||||
if (max < priceData[i].price) {
|
||||
max = priceData[i].price
|
||||
}
|
||||
}
|
||||
let option = {
|
||||
padding: [0, 0, 0, 0],
|
||||
grid: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
label: {
|
||||
backgroundColor: '#6a7985'
|
||||
}
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
show: false,
|
||||
type: 'category',
|
||||
data: category
|
||||
},
|
||||
yAxis: {
|
||||
show: false,
|
||||
type: 'value',
|
||||
min: (min).toFixed(2),
|
||||
max: (max).toFixed(2),
|
||||
minInterval: 0.01,
|
||||
},
|
||||
// visualMap: {
|
||||
// show: false,
|
||||
// type: 'piecewise',
|
||||
// pieces: [
|
||||
// {
|
||||
// min: Number(min),
|
||||
// max: Number(openPrice),
|
||||
// color: 'green'
|
||||
// },
|
||||
// {
|
||||
// min: Number(openPrice),
|
||||
// max: Number(max),
|
||||
// color: 'red'
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
series: [
|
||||
{
|
||||
data: price,
|
||||
type: 'line',
|
||||
smooth: false,
|
||||
stack: '总量',
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
color: lastPrice > openPrice ? 'rgba(245, 0, 0, 1)' : 'rgb(6,251,10)'
|
||||
},
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: lastPrice > openPrice ? 'rgba(245, 0, 0, 1)' : 'rgba(6,251,10, 1)'
|
||||
}, {
|
||||
offset: 1,
|
||||
color: lastPrice > openPrice ? 'rgba(245, 0, 0, 0.25)' : 'rgba(6,251,10, 0.25)'
|
||||
}])
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
chart.setOption(option);
|
||||
})
|
||||
}
|
||||
const chart =ref( null)
|
||||
|
||||
onMounted(() => {
|
||||
chart.value = echarts.init( document.getElementById('sparkLine'+stockCode));
|
||||
setChartData(chart.value);
|
||||
})
|
||||
|
||||
|
||||
watchEffect(() => {
|
||||
console.log(stockName,'lastPrice变化为:', lastPrice,lastPrice > openPrice)
|
||||
setChartData(chart.value);
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
<template>
|
||||
<div style="height: 20px;width: 100%" :id="'sparkLine'+stockCode">
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,25 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {h} from 'vue'
|
||||
import {NTag,NImage} from 'naive-ui'
|
||||
import EmbeddedUrl from "./EmbeddedUrl.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-tabs type="line" animated>
|
||||
<n-tab-pane name="百度股市通" tab="百度股市通">
|
||||
<embedded-url url="https://gushitong.baidu.com" :height="'calc(100vh - 252px)'"/>
|
||||
<n-tab-pane name="选股通" tab="选股通">
|
||||
<embedded-url url="https://xuangutong.com.cn" :height="'calc(100vh - 252px)'"/>
|
||||
</n-tab-pane>
|
||||
<!-- <n-tab-pane name="百度股市通" tab="百度股市通">-->
|
||||
<!-- <embedded-url url="https://gushitong.baidu.com" :height="'calc(100vh - 252px)'"/>-->
|
||||
<!-- </n-tab-pane>-->
|
||||
<n-tab-pane name="东财大盘星图" tab="东财大盘星图">
|
||||
<embedded-url url="https://quote.eastmoney.com/stockhotmap/" :height="'calc(100vh - 252px)'"/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="TopHub" tab="TopHub(今日热榜)">
|
||||
<embedded-url url="https://tophub.today/c/finance" :height="'calc(100vh - 252px)'"/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="选股通" tab="选股通">
|
||||
<embedded-url url="https://xuangutong.com.cn" :height="'calc(100vh - 252px)'"/>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="摸鱼" tab="摸鱼">
|
||||
<embedded-url url="https://996.ninja/" :height="'calc(100vh - 252px)'"/>
|
||||
</n-tab-pane>
|
||||
<!-- <n-tab-pane name="摸鱼" tab="摸鱼">-->
|
||||
<!-- <embedded-url url="https://996.ninja/" :height="'calc(100vh - 252px)'"/>-->
|
||||
<!-- </n-tab-pane>-->
|
||||
|
||||
|
||||
<n-tab-pane name="欢迎推荐更多有趣的财经网页" tab="欢迎推荐更多有趣的财经网页">
|
||||
|
||||
1
go.mod
1
go.mod
@@ -13,6 +13,7 @@ require (
|
||||
github.com/go-ego/gse v0.80.3
|
||||
github.com/go-resty/resty/v2 v2.16.2
|
||||
github.com/go-toast/toast v0.0.0-20190211030409-01e6764cf0a4
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf
|
||||
github.com/robertkrimen/otto v0.5.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/samber/lo v1.49.1
|
||||
|
||||
2
go.sum
2
go.sum
@@ -57,6 +57,8 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbu
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf h1:WfD7VjIE6z8dIvMsI4/s+1qr5EL+zoIGev1BQj1eoJ8=
|
||||
github.com/inconshreveable/go-update v0.0.0-20160112193335-8152e7eb6ccf/go.mod h1:hyb9oH7vZsitZCiBt0ZvifOrB+qc8PS5IiilCIb87rg=
|
||||
github.com/jackmordaunt/icns/v3 v3.0.1 h1:xxot6aNuGrU+lNgxz5I5H0qSeCjNKp8uTXB1j8D4S3o=
|
||||
github.com/jackmordaunt/icns/v3 v3.0.1/go.mod h1:5sHL59nqTd2ynTnowxB/MDQFhKNqkK8X687uKNygaSQ=
|
||||
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
|
||||
|
||||
23
utils.go
Normal file
23
utils.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
// @Author spark
|
||||
// @Date 2025/7/8 18:51
|
||||
// @Desc
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
import "runtime"
|
||||
|
||||
// IsWindows 判断是否为 Windows 系统
|
||||
func IsWindows() bool {
|
||||
return runtime.GOOS == "windows"
|
||||
}
|
||||
|
||||
// IsMacOS 判断是否为 macOS 系统
|
||||
func IsMacOS() bool {
|
||||
return runtime.GOOS == "darwin"
|
||||
}
|
||||
|
||||
// IsLinux 判断是否为 Linux 系统
|
||||
func IsLinux() bool {
|
||||
return runtime.GOOS == "linux"
|
||||
}
|
||||
Reference in New Issue
Block a user