From 54b0c7ccb34751fc1ddea0f4727690b4d8dd286a Mon Sep 17 00:00:00 2001 From: ArvinLovegood Date: Sun, 30 Mar 2025 08:58:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(stock):=E6=B7=BB=E5=8A=A0=E8=82=A1?= =?UTF-8?q?=E7=A5=A8=E8=87=AA=E5=8A=A8=E5=88=86=E6=9E=90=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在 App 结构中添加 cron 实例,用于定时任务调度 - 新增 SetStockAICron 函数,用于设置股票自动分析的 cron 表达式- 在前端 stock 组件中添加 cron 字段,允许用户输入定时任务规则 - 在后端 StockDataApi 中添加 SetStockAICron 方法,用于更新数据库中的 cron 信息 - 修改前端保存逻辑,当用户设置 cron 时,调用 SetStockAICron接口保存 --- app.go | 48 +++++++++++++++++++++++++++++-- backend/data/openai_api.go | 2 -- backend/data/openai_api_test.go | 2 +- backend/data/stock_data_api.go | 10 ++++++- frontend/src/components/stock.vue | 12 +++++++- frontend/wailsjs/go/main/App.d.ts | 2 ++ frontend/wailsjs/go/main/App.js | 4 +++ go.mod | 1 + go.sum | 2 ++ 9 files changed, 76 insertions(+), 7 deletions(-) diff --git a/app.go b/app.go index b0b0552..639aac2 100644 --- a/app.go +++ b/app.go @@ -15,6 +15,7 @@ import ( "github.com/getlantern/systray" "github.com/go-resty/resty/v2" "github.com/go-toast/toast" + "github.com/robfig/cron/v3" "github.com/wailsapp/wails/v2/pkg/options" "github.com/wailsapp/wails/v2/pkg/runtime" "go-stock/backend/data" @@ -31,14 +32,18 @@ import ( type App struct { ctx context.Context cache *freecache.Cache + cron *cron.Cron } // NewApp creates a new App application struct func NewApp() *App { cacheSize := 512 * 1024 cache := freecache.NewCache(cacheSize) + c := cron.New(cron.WithSeconds()) + c.Start() return &App{ cache: cache, + cron: c, } } @@ -58,7 +63,6 @@ func (a *App) startup(ctx context.Context) { }, func() { go onExit(a) }) - } func (a *App) CheckUpdate() { @@ -139,6 +143,11 @@ func (a *App) domReady(ctx context.Context) { //检查新版本 go func() { 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) // } //}() + 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 { @@ -480,6 +521,7 @@ func (a *App) shutdown(ctx context.Context) { defer PanicHandler() // Perform your teardown here systray.Quit() + a.cron.Stop() os.Exit(0) } @@ -818,7 +860,9 @@ func (a *App) AddPrompt(prompt models.Prompt) string { func (a *App) DelPrompt(id uint) string { return data.NewPromptTemplateApi().DelPrompt(id) } - +func (a *App) SetStockAICron(cron, stockCode string) { + data.NewStockDataApi().SetStockAICron(cron, stockCode) +} func OnSecondInstanceLaunch(secondInstanceData options.SecondInstanceData) { notification := toast.Notification{ AppID: "go-stock", diff --git a/backend/data/openai_api.go b/backend/data/openai_api.go index 93ddad4..cfc4f88 100644 --- a/backend/data/openai_api.go +++ b/backend/data/openai_api.go @@ -114,8 +114,6 @@ func (o OpenAi) NewChatStream(stock, stockCode, userQuestion string, sysPromptId }() defer close(ch) - logger.SugaredLogger.Errorf("NewChatStream stock:%s stockCode:%s,sysPromptId:%d", stock, stockCode, *sysPromptId) - sysPrompt := "" if sysPromptId == nil || *sysPromptId == 0 { sysPrompt = o.Prompt diff --git a/backend/data/openai_api_test.go b/backend/data/openai_api_test.go index ba0f317..a02c5ad 100644 --- a/backend/data/openai_api_test.go +++ b/backend/data/openai_api_test.go @@ -8,7 +8,7 @@ import ( func TestNewDeepSeekOpenAiConfig(t *testing.T) { //db.Init("../../data/stock.db") ai := NewDeepSeekOpenAi(context.TODO()) - res := ai.NewChatStream("上海贝岭", "sh600171", "分析以上股票资金流入信息,找出适合买入的股票,给出具体操作建议") + res := ai.NewChatStream("上海贝岭", "sh600171", "分析以上股票资金流入信息,找出适合买入的股票,给出具体操作建议", nil) for { select { case msg := <-res: diff --git a/backend/data/stock_data_api.go b/backend/data/stock_data_api.go index cb90256..f4e57ba 100644 --- a/backend/data/stock_data_api.go +++ b/backend/data/stock_data_api.go @@ -163,6 +163,7 @@ type FollowedStock struct { AlarmPrice float64 Time time.Time Sort int64 + Cron string 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) } - +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 { var result *[]FollowedStock db.Dao.Model(&FollowedStock{}).Order("sort asc,time desc").Find(&result) diff --git a/frontend/src/components/stock.vue b/frontend/src/components/stock.vue index 5f5f7fa..6fb35f1 100644 --- a/frontend/src/components/stock.vue +++ b/frontend/src/components/stock.vue @@ -15,7 +15,7 @@ import { SetCostPriceAndVolume, SetStockSort, UnFollow, - ShareAnalysis, SaveAsMarkdown, GetPromptTemplates + ShareAnalysis, SaveAsMarkdown, GetPromptTemplates, SetStockAICron } from '../../wailsjs/go/main/App' import { NAvatar, @@ -81,6 +81,7 @@ const formModel = ref({ alarm: 0, alarmPrice:0, sort:999, + cron:"", }) const promptTemplates=ref([]) const sysPromptOptions=ref([]) @@ -545,6 +546,7 @@ function setStock(code,name){ formModel.value.alarm=res[0].AlarmChangePercent formModel.value.alarmPrice=res[0].AlarmPrice formModel.value.sort=res[0].Sort + formModel.value.cron=res[0].Cron modalShow.value=true } @@ -587,6 +589,11 @@ function updateCostPriceAndVolumeNew(code,price,volume,alarm,formModel){ //message.success(result) }) } + if(formModel.cron){ + SetStockAICron(formModel.cron,code).then(result => { + //message.success(result) + }) + } if(alarm||formModel.alarmPrice){ SetAlarmChangePercent(alarm,formModel.alarmPrice,code).then(result => { @@ -975,6 +982,9 @@ function share(code,name){ + + +