mirror of
https://github.com/ArvinLovegood/go-stock.git
synced 2025-07-19 00:00:09 +08:00
feat(stock):添加股票自动分析功能
- 在 App 结构中添加 cron 实例,用于定时任务调度 - 新增 SetStockAICron 函数,用于设置股票自动分析的 cron 表达式- 在前端 stock 组件中添加 cron 字段,允许用户输入定时任务规则 - 在后端 StockDataApi 中添加 SetStockAICron 方法,用于更新数据库中的 cron 信息 - 修改前端保存逻辑,当用户设置 cron 时,调用 SetStockAICron接口保存
This commit is contained in:
parent
e44bc55301
commit
54b0c7ccb3
48
app.go
48
app.go
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/getlantern/systray"
|
"github.com/getlantern/systray"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/go-toast/toast"
|
"github.com/go-toast/toast"
|
||||||
|
"github.com/robfig/cron/v3"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
"github.com/wailsapp/wails/v2/pkg/options"
|
||||||
"github.com/wailsapp/wails/v2/pkg/runtime"
|
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||||
"go-stock/backend/data"
|
"go-stock/backend/data"
|
||||||
@ -31,14 +32,18 @@ import (
|
|||||||
type App struct {
|
type App struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cache *freecache.Cache
|
cache *freecache.Cache
|
||||||
|
cron *cron.Cron
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewApp creates a new App application struct
|
// NewApp creates a new App application struct
|
||||||
func NewApp() *App {
|
func NewApp() *App {
|
||||||
cacheSize := 512 * 1024
|
cacheSize := 512 * 1024
|
||||||
cache := freecache.NewCache(cacheSize)
|
cache := freecache.NewCache(cacheSize)
|
||||||
|
c := cron.New(cron.WithSeconds())
|
||||||
|
c.Start()
|
||||||
return &App{
|
return &App{
|
||||||
cache: cache,
|
cache: cache,
|
||||||
|
cron: c,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +63,6 @@ func (a *App) startup(ctx context.Context) {
|
|||||||
}, func() {
|
}, func() {
|
||||||
go onExit(a)
|
go onExit(a)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) CheckUpdate() {
|
func (a *App) CheckUpdate() {
|
||||||
@ -139,6 +143,11 @@ func (a *App) domReady(ctx context.Context) {
|
|||||||
//检查新版本
|
//检查新版本
|
||||||
go func() {
|
go func() {
|
||||||
a.CheckUpdate()
|
a.CheckUpdate()
|
||||||
|
a.cron.AddFunc("30 05 8,12,20 * * *", func() {
|
||||||
|
logger.SugaredLogger.Errorf("Checking for updates...")
|
||||||
|
a.CheckUpdate()
|
||||||
|
})
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
//检查谷歌浏览器
|
//检查谷歌浏览器
|
||||||
@ -158,6 +167,38 @@ func (a *App) domReady(ctx context.Context) {
|
|||||||
// logger.SugaredLogger.Infof("Edge浏览器已安装,路径为: %s", path)
|
// logger.SugaredLogger.Infof("Edge浏览器已安装,路径为: %s", path)
|
||||||
// }
|
// }
|
||||||
//}()
|
//}()
|
||||||
|
followList := data.NewStockDataApi().GetFollowList()
|
||||||
|
for _, follow := range *followList {
|
||||||
|
if follow.Cron == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
logger.SugaredLogger.Errorf("添加自动分析任务:%s cron=%s", follow.Name, follow.Cron)
|
||||||
|
a.cron.AddFunc(follow.Cron, func() {
|
||||||
|
go runtime.EventsEmit(a.ctx, "warnMsg", "开始自动分析"+follow.Name+"_"+follow.StockCode)
|
||||||
|
ai := data.NewDeepSeekOpenAi(a.ctx)
|
||||||
|
msgs := ai.NewChatStream(follow.Name, follow.StockCode, "", nil)
|
||||||
|
var res strings.Builder
|
||||||
|
|
||||||
|
chatId := ""
|
||||||
|
question := ""
|
||||||
|
for msg := range msgs {
|
||||||
|
if msg["extraContent"] != nil {
|
||||||
|
res.WriteString(msg["extraContent"].(string) + "\n")
|
||||||
|
}
|
||||||
|
if msg["content"] != nil {
|
||||||
|
res.WriteString(msg["content"].(string))
|
||||||
|
}
|
||||||
|
if msg["chatId"] != nil {
|
||||||
|
chatId = msg["chatId"].(string)
|
||||||
|
}
|
||||||
|
if msg["question"] != nil {
|
||||||
|
question = msg["question"].(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.NewDeepSeekOpenAi(a.ctx).SaveAIResponseResult(follow.StockCode, follow.Name, res.String(), chatId, question)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshTelegraphList() *[]string {
|
func refreshTelegraphList() *[]string {
|
||||||
@ -480,6 +521,7 @@ func (a *App) shutdown(ctx context.Context) {
|
|||||||
defer PanicHandler()
|
defer PanicHandler()
|
||||||
// Perform your teardown here
|
// Perform your teardown here
|
||||||
systray.Quit()
|
systray.Quit()
|
||||||
|
a.cron.Stop()
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -818,7 +860,9 @@ func (a *App) AddPrompt(prompt models.Prompt) string {
|
|||||||
func (a *App) DelPrompt(id uint) string {
|
func (a *App) DelPrompt(id uint) string {
|
||||||
return data.NewPromptTemplateApi().DelPrompt(id)
|
return data.NewPromptTemplateApi().DelPrompt(id)
|
||||||
}
|
}
|
||||||
|
func (a *App) SetStockAICron(cron, stockCode string) {
|
||||||
|
data.NewStockDataApi().SetStockAICron(cron, stockCode)
|
||||||
|
}
|
||||||
func OnSecondInstanceLaunch(secondInstanceData options.SecondInstanceData) {
|
func OnSecondInstanceLaunch(secondInstanceData options.SecondInstanceData) {
|
||||||
notification := toast.Notification{
|
notification := toast.Notification{
|
||||||
AppID: "go-stock",
|
AppID: "go-stock",
|
||||||
|
@ -114,8 +114,6 @@ func (o OpenAi) NewChatStream(stock, stockCode, userQuestion string, sysPromptId
|
|||||||
}()
|
}()
|
||||||
defer close(ch)
|
defer close(ch)
|
||||||
|
|
||||||
logger.SugaredLogger.Errorf("NewChatStream stock:%s stockCode:%s,sysPromptId:%d", stock, stockCode, *sysPromptId)
|
|
||||||
|
|
||||||
sysPrompt := ""
|
sysPrompt := ""
|
||||||
if sysPromptId == nil || *sysPromptId == 0 {
|
if sysPromptId == nil || *sysPromptId == 0 {
|
||||||
sysPrompt = o.Prompt
|
sysPrompt = o.Prompt
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
func TestNewDeepSeekOpenAiConfig(t *testing.T) {
|
func TestNewDeepSeekOpenAiConfig(t *testing.T) {
|
||||||
//db.Init("../../data/stock.db")
|
//db.Init("../../data/stock.db")
|
||||||
ai := NewDeepSeekOpenAi(context.TODO())
|
ai := NewDeepSeekOpenAi(context.TODO())
|
||||||
res := ai.NewChatStream("上海贝岭", "sh600171", "分析以上股票资金流入信息,找出适合买入的股票,给出具体操作建议")
|
res := ai.NewChatStream("上海贝岭", "sh600171", "分析以上股票资金流入信息,找出适合买入的股票,给出具体操作建议", nil)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case msg := <-res:
|
case msg := <-res:
|
||||||
|
@ -163,6 +163,7 @@ type FollowedStock struct {
|
|||||||
AlarmPrice float64
|
AlarmPrice float64
|
||||||
Time time.Time
|
Time time.Time
|
||||||
Sort int64
|
Sort int64
|
||||||
|
Cron string
|
||||||
IsDel soft_delete.DeletedAt `gorm:"softDelete:flag"`
|
IsDel soft_delete.DeletedAt `gorm:"softDelete:flag"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,7 +419,14 @@ func (receiver StockDataApi) SetStockSort(sort int64, stockCode string) {
|
|||||||
}
|
}
|
||||||
db.Dao.Model(&FollowedStock{}).Where("stock_code = ?", strings.ToLower(stockCode)).Update("sort", sort)
|
db.Dao.Model(&FollowedStock{}).Where("stock_code = ?", strings.ToLower(stockCode)).Update("sort", sort)
|
||||||
}
|
}
|
||||||
|
func (receiver StockDataApi) SetStockAICron(cron string, stockCode string) {
|
||||||
|
if strutil.HasPrefixAny(stockCode, []string{"gb_"}) {
|
||||||
|
stockCode = strings.ToUpper(stockCode)
|
||||||
|
stockCode = strings.Replace(stockCode, "gb_", "us", 1)
|
||||||
|
stockCode = strings.Replace(stockCode, "GB_", "us", 1)
|
||||||
|
}
|
||||||
|
db.Dao.Model(&FollowedStock{}).Where("stock_code = ?", strings.ToLower(stockCode)).Update("cron", cron)
|
||||||
|
}
|
||||||
func (receiver StockDataApi) GetFollowList() *[]FollowedStock {
|
func (receiver StockDataApi) GetFollowList() *[]FollowedStock {
|
||||||
var result *[]FollowedStock
|
var result *[]FollowedStock
|
||||||
db.Dao.Model(&FollowedStock{}).Order("sort asc,time desc").Find(&result)
|
db.Dao.Model(&FollowedStock{}).Order("sort asc,time desc").Find(&result)
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
SetCostPriceAndVolume,
|
SetCostPriceAndVolume,
|
||||||
SetStockSort,
|
SetStockSort,
|
||||||
UnFollow,
|
UnFollow,
|
||||||
ShareAnalysis, SaveAsMarkdown, GetPromptTemplates
|
ShareAnalysis, SaveAsMarkdown, GetPromptTemplates, SetStockAICron
|
||||||
} from '../../wailsjs/go/main/App'
|
} from '../../wailsjs/go/main/App'
|
||||||
import {
|
import {
|
||||||
NAvatar,
|
NAvatar,
|
||||||
@ -81,6 +81,7 @@ const formModel = ref({
|
|||||||
alarm: 0,
|
alarm: 0,
|
||||||
alarmPrice:0,
|
alarmPrice:0,
|
||||||
sort:999,
|
sort:999,
|
||||||
|
cron:"",
|
||||||
})
|
})
|
||||||
const promptTemplates=ref([])
|
const promptTemplates=ref([])
|
||||||
const sysPromptOptions=ref([])
|
const sysPromptOptions=ref([])
|
||||||
@ -545,6 +546,7 @@ function setStock(code,name){
|
|||||||
formModel.value.alarm=res[0].AlarmChangePercent
|
formModel.value.alarm=res[0].AlarmChangePercent
|
||||||
formModel.value.alarmPrice=res[0].AlarmPrice
|
formModel.value.alarmPrice=res[0].AlarmPrice
|
||||||
formModel.value.sort=res[0].Sort
|
formModel.value.sort=res[0].Sort
|
||||||
|
formModel.value.cron=res[0].Cron
|
||||||
modalShow.value=true
|
modalShow.value=true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,6 +589,11 @@ function updateCostPriceAndVolumeNew(code,price,volume,alarm,formModel){
|
|||||||
//message.success(result)
|
//message.success(result)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if(formModel.cron){
|
||||||
|
SetStockAICron(formModel.cron,code).then(result => {
|
||||||
|
//message.success(result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if(alarm||formModel.alarmPrice){
|
if(alarm||formModel.alarmPrice){
|
||||||
SetAlarmChangePercent(alarm,formModel.alarmPrice,code).then(result => {
|
SetAlarmChangePercent(alarm,formModel.alarmPrice,code).then(result => {
|
||||||
@ -975,6 +982,9 @@ function share(code,name){
|
|||||||
<n-input-number v-model:value="formModel.sort" min="0" placeholder="请输入股价排序值" >
|
<n-input-number v-model:value="formModel.sort" min="0" placeholder="请输入股价排序值" >
|
||||||
</n-input-number>
|
</n-input-number>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
|
<n-form-item label="AI cron" path="cron">
|
||||||
|
<n-input v-model:value="formModel.cron" placeholder="请输入cron表达式" />
|
||||||
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<n-button type="primary" @click="updateCostPriceAndVolumeNew(formModel.code,formModel.costPrice,formModel.volume,formModel.alarm,formModel)">保存</n-button>
|
<n-button type="primary" @click="updateCostPriceAndVolumeNew(formModel.code,formModel.costPrice,formModel.volume,formModel.alarm,formModel)">保存</n-button>
|
||||||
|
2
frontend/wailsjs/go/main/App.d.ts
vendored
2
frontend/wailsjs/go/main/App.d.ts
vendored
@ -47,6 +47,8 @@ export function SetAlarmChangePercent(arg1:number,arg2:number,arg3:string):Promi
|
|||||||
|
|
||||||
export function SetCostPriceAndVolume(arg1:string,arg2:number,arg3:number):Promise<string>;
|
export function SetCostPriceAndVolume(arg1:string,arg2:number,arg3:number):Promise<string>;
|
||||||
|
|
||||||
|
export function SetStockAICron(arg1:string,arg2:string):Promise<void>;
|
||||||
|
|
||||||
export function SetStockSort(arg1:number,arg2:string):Promise<void>;
|
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>;
|
||||||
|
@ -90,6 +90,10 @@ export function SetCostPriceAndVolume(arg1, arg2, arg3) {
|
|||||||
return window['go']['main']['App']['SetCostPriceAndVolume'](arg1, arg2, arg3);
|
return window['go']['main']['App']['SetCostPriceAndVolume'](arg1, arg2, arg3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function SetStockAICron(arg1, arg2) {
|
||||||
|
return window['go']['main']['App']['SetStockAICron'](arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
export function SetStockSort(arg1, arg2) {
|
export function SetStockSort(arg1, arg2) {
|
||||||
return window['go']['main']['App']['SetStockSort'](arg1, arg2);
|
return window['go']['main']['App']['SetStockSort'](arg1, arg2);
|
||||||
}
|
}
|
||||||
|
1
go.mod
1
go.mod
@ -13,6 +13,7 @@ require (
|
|||||||
github.com/glebarez/sqlite v1.11.0
|
github.com/glebarez/sqlite v1.11.0
|
||||||
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/robfig/cron/v3 v3.0.1
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.10.0
|
||||||
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
|
||||||
|
2
go.sum
2
go.sum
@ -125,6 +125,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
|
|||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
||||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||||
|
Loading…
x
Reference in New Issue
Block a user