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/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",
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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){
|
||||
<n-input-number v-model:value="formModel.sort" min="0" placeholder="请输入股价排序值" >
|
||||
</n-input-number>
|
||||
</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>
|
||||
<template #footer>
|
||||
<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 SetStockAICron(arg1:string,arg2:string):Promise<void>;
|
||||
|
||||
export function SetStockSort(arg1:number,arg2:string):Promise<void>;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
export function SetStockAICron(arg1, arg2) {
|
||||
return window['go']['main']['App']['SetStockAICron'](arg1, arg2);
|
||||
}
|
||||
|
||||
export function 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/go-resty/resty/v2 v2.16.2
|
||||
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/wailsapp/wails/v2 v2.10.1
|
||||
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.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
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/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
|
Loading…
x
Reference in New Issue
Block a user