diff --git a/app.go b/app.go index 639aac2..21467ba 100644 --- a/app.go +++ b/app.go @@ -30,9 +30,10 @@ import ( // App struct type App struct { - ctx context.Context - cache *freecache.Cache - cron *cron.Cron + ctx context.Context + cache *freecache.Cache + cron *cron.Cron + cronEntrys map[string]cron.EntryID } // NewApp creates a new App application struct @@ -42,8 +43,9 @@ func NewApp() *App { c := cron.New(cron.WithSeconds()) c.Start() return &App{ - cache: cache, - cron: c, + cache: cache, + cron: c, + cronEntrys: make(map[string]cron.EntryID), } } @@ -172,35 +174,45 @@ func (a *App) domReady(ctx context.Context) { 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) - }) + entryID, err := a.cron.AddFunc(follow.Cron, a.AddCronTask(follow)) + logger.SugaredLogger.Errorf("添加自动分析任务:%s cron=%s entryID:%v", follow.Name, follow.Cron, entryID) + a.cronEntrys[follow.StockCode] = entryID + if err != nil { + return + } } } +func (a *App) AddCronTask(follow data.FollowedStock) func() { + return 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) + go runtime.EventsEmit(a.ctx, "warnMsg", "AI分析完成:"+follow.Name+"_"+follow.StockCode) + + } +} + func refreshTelegraphList() *[]string { url := "https://www.cls.cn/telegraph" response, err := resty.New().R(). @@ -860,8 +872,18 @@ 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 (a *App) SetStockAICron(cronText, stockCode string) { + data.NewStockDataApi().SetStockAICron(cronText, stockCode) + if strutil.HasPrefixAny(stockCode, []string{"gb_"}) { + stockCode = strings.ToUpper(stockCode) + stockCode = strings.Replace(stockCode, "gb_", "us", 1) + stockCode = strings.Replace(stockCode, "GB_", "us", 1) + } + if entryID, exists := a.cronEntrys[stockCode]; exists { + a.cron.Remove(entryID) + } + follow := data.NewStockDataApi().GetFollowedStockByStockCode(stockCode) + a.cron.AddFunc(cronText, a.AddCronTask(follow)) } func OnSecondInstanceLaunch(secondInstanceData options.SecondInstanceData) { notification := toast.Notification{ diff --git a/backend/data/stock_data_api.go b/backend/data/stock_data_api.go index f4e57ba..f1ecfbe 100644 --- a/backend/data/stock_data_api.go +++ b/backend/data/stock_data_api.go @@ -426,6 +426,7 @@ func (receiver StockDataApi) SetStockAICron(cron string, stockCode string) { 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 @@ -475,6 +476,12 @@ func (receiver StockDataApi) GetStockList(key string) []StockBasic { return result } +func (receiver StockDataApi) GetFollowedStockByStockCode(code string) FollowedStock { + var result FollowedStock + db.Dao.Model(&FollowedStock{}).Where("stock_code = ?", strings.ToLower(code)).First(&result) + return result +} + // GB18030ToUTF8 GB18030 转换为 UTF8 func GB18030ToUTF8(bs []byte) string { reader := transform.NewReader(bytes.NewReader(bs), simplifiedchinese.GB18030.NewDecoder()) diff --git a/frontend/wailsjs/go/main/App.d.ts b/frontend/wailsjs/go/main/App.d.ts index e222a62..2420d60 100644 --- a/frontend/wailsjs/go/main/App.d.ts +++ b/frontend/wailsjs/go/main/App.d.ts @@ -1,7 +1,9 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT -import {models} from '../models'; import {data} from '../models'; +import {models} from '../models'; + +export function AddCronTask(arg1:data.FollowedStock):Promise; export function AddPrompt(arg1:models.Prompt):Promise; diff --git a/frontend/wailsjs/go/main/App.js b/frontend/wailsjs/go/main/App.js index e1d62ae..f26cb44 100644 --- a/frontend/wailsjs/go/main/App.js +++ b/frontend/wailsjs/go/main/App.js @@ -2,6 +2,10 @@ // Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL // This file is automatically generated. DO NOT EDIT +export function AddCronTask(arg1) { + return window['go']['main']['App']['AddCronTask'](arg1); +} + export function AddPrompt(arg1) { return window['go']['main']['App']['AddPrompt'](arg1); } diff --git a/frontend/wailsjs/go/models.ts b/frontend/wailsjs/go/models.ts index 05a76d1..23f2c99 100644 --- a/frontend/wailsjs/go/models.ts +++ b/frontend/wailsjs/go/models.ts @@ -142,6 +142,61 @@ export namespace data { return a; } } + export class FollowedStock { + StockCode: string; + Name: string; + Volume: number; + CostPrice: number; + Price: number; + PriceChange: number; + ChangePercent: number; + AlarmChangePercent: number; + AlarmPrice: number; + // Go type: time + Time: any; + Sort: number; + Cron: string; + IsDel: number; + + static createFrom(source: any = {}) { + return new FollowedStock(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.StockCode = source["StockCode"]; + this.Name = source["Name"]; + this.Volume = source["Volume"]; + this.CostPrice = source["CostPrice"]; + this.Price = source["Price"]; + this.PriceChange = source["PriceChange"]; + this.ChangePercent = source["ChangePercent"]; + this.AlarmChangePercent = source["AlarmChangePercent"]; + this.AlarmPrice = source["AlarmPrice"]; + this.Time = this.convertValues(source["Time"], null); + this.Sort = source["Sort"]; + this.Cron = source["Cron"]; + this.IsDel = source["IsDel"]; + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice && a.map) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } export class Settings { ID: number;