mirror of
https://github.com/ArvinLovegood/go-stock.git
synced 2025-07-19 00:00:09 +08:00
feat(settings): 添加推送设置功能- 新增本地推送和钉钉推送的配置选项
- 实现配置的保存和读取功能- 添加测试通知按钮 -优化股票信息的显示格式
This commit is contained in:
parent
7c52cd1d26
commit
9dc8fa97df
13
app.go
13
app.go
@ -146,9 +146,9 @@ func GetStockInfos(follows ...data.FollowedStock) *[]data.StockInfo {
|
||||
func getStockInfo(follow data.FollowedStock) *data.StockInfo {
|
||||
stockCode := follow.StockCode
|
||||
stockDatas, err := data.NewStockDataApi().GetStockCodeRealTimeData(stockCode)
|
||||
if err != nil {
|
||||
if err != nil || len(*stockDatas) == 0 {
|
||||
logger.SugaredLogger.Errorf("get stock code real time data error:%s", err.Error())
|
||||
return nil
|
||||
return &data.StockInfo{}
|
||||
}
|
||||
stockData := (*stockDatas)[0]
|
||||
addStockFollowData(follow, &stockData)
|
||||
@ -413,3 +413,12 @@ func onReady(a *App) {
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (a *App) UpdateConfig(settings *data.Settings) string {
|
||||
logger.SugaredLogger.Infof("UpdateConfig:%+v", settings)
|
||||
return data.NewSettingsApi(settings).UpdateConfig()
|
||||
}
|
||||
|
||||
func (a *App) GetConfig() *data.Settings {
|
||||
return data.NewSettingsApi(&data.Settings{}).GetConfig()
|
||||
}
|
||||
|
@ -184,3 +184,10 @@ func getMsgTypeName(msgType int) string {
|
||||
return "未知类型"
|
||||
}
|
||||
}
|
||||
func (a *App) UpdateConfig(settings *data.Settings) string {
|
||||
return data.NewSettingsApi(settings).UpdateConfig()
|
||||
}
|
||||
|
||||
func (a *App) GetConfig() *data.Settings {
|
||||
return data.NewSettingsApi(&data.Settings{}).GetConfig()
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
// @Date 2025/1/8 9:40
|
||||
// @Desc
|
||||
// -----------------------------------------------------------------------------------
|
||||
type alertWindowsApi struct {
|
||||
type AlertWindowsApi struct {
|
||||
AppID string
|
||||
// 窗口标题
|
||||
Title string
|
||||
@ -21,8 +21,8 @@ type alertWindowsApi struct {
|
||||
Icon string
|
||||
}
|
||||
|
||||
func NewAlertWindowsApi(AppID string, Title string, Content string, Icon string) *alertWindowsApi {
|
||||
return &alertWindowsApi{
|
||||
func NewAlertWindowsApi(AppID string, Title string, Content string, Icon string) *AlertWindowsApi {
|
||||
return &AlertWindowsApi{
|
||||
AppID: AppID,
|
||||
Title: Title,
|
||||
Content: Content,
|
||||
@ -30,7 +30,12 @@ func NewAlertWindowsApi(AppID string, Title string, Content string, Icon string)
|
||||
}
|
||||
}
|
||||
|
||||
func (a alertWindowsApi) SendNotification() bool {
|
||||
func (a AlertWindowsApi) SendNotification() bool {
|
||||
if getConfig().LocalPushEnable == false {
|
||||
logger.SugaredLogger.Error("本地推送未开启")
|
||||
return false
|
||||
}
|
||||
|
||||
notification := toast.Notification{
|
||||
AppID: a.AppID,
|
||||
Title: a.Title,
|
||||
|
@ -10,8 +10,6 @@ import (
|
||||
// @Desc
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
const dingding_robot_url = "https://oapi.dingtalk.com/robot/send?access_token=0237527b404598f37ae5d83ef36e936860c7ba5d3892cd43f64c4159d3ed7cb1"
|
||||
|
||||
type DingDingAPI struct {
|
||||
client *resty.Client
|
||||
}
|
||||
@ -23,11 +21,15 @@ func NewDingDingAPI() *DingDingAPI {
|
||||
}
|
||||
|
||||
func (DingDingAPI) SendDingDingMessage(message string) string {
|
||||
if getConfig().DingPushEnable == false {
|
||||
logger.SugaredLogger.Info("钉钉推送未开启")
|
||||
return "钉钉推送未开启"
|
||||
}
|
||||
// 发送钉钉消息
|
||||
resp, err := resty.New().R().
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(message).
|
||||
Post(dingding_robot_url)
|
||||
Post(getApiURL())
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
return "发送钉钉消息失败"
|
||||
@ -35,6 +37,12 @@ func (DingDingAPI) SendDingDingMessage(message string) string {
|
||||
logger.SugaredLogger.Infof("send dingding message: %s", resp.String())
|
||||
return "发送钉钉消息成功"
|
||||
}
|
||||
func getConfig() *Settings {
|
||||
return NewSettingsApi(&Settings{}).GetConfig()
|
||||
}
|
||||
func getApiURL() string {
|
||||
return getConfig().DingRobot
|
||||
}
|
||||
|
||||
func (DingDingAPI) SendToDingDing(title, message string) string {
|
||||
// 发送钉钉消息
|
||||
@ -50,7 +58,7 @@ func (DingDingAPI) SendToDingDing(title, message string) string {
|
||||
IsAtAll: true,
|
||||
},
|
||||
}).
|
||||
Post(dingding_robot_url)
|
||||
Post(getApiURL())
|
||||
if err != nil {
|
||||
logger.SugaredLogger.Error(err.Error())
|
||||
return "发送钉钉消息失败"
|
||||
|
44
backend/data/settings_api.go
Normal file
44
backend/data/settings_api.go
Normal file
@ -0,0 +1,44 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"go-stock/backend/db"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Settings struct {
|
||||
gorm.Model
|
||||
LocalPushEnable bool `json:"localPushEnable"`
|
||||
DingPushEnable bool `json:"dingPushEnable"`
|
||||
DingRobot string `json:"dingRobot"`
|
||||
}
|
||||
|
||||
func (receiver Settings) TableName() string {
|
||||
return "settings"
|
||||
}
|
||||
|
||||
type SettingsApi struct {
|
||||
Config Settings
|
||||
}
|
||||
|
||||
func NewSettingsApi(settings *Settings) *SettingsApi {
|
||||
return &SettingsApi{
|
||||
Config: *settings,
|
||||
}
|
||||
}
|
||||
|
||||
func (s SettingsApi) UpdateConfig() string {
|
||||
err := db.Dao.Model(s.Config).Updates(map[string]any{
|
||||
"local_push_enable": s.Config.LocalPushEnable,
|
||||
"ding_push_enable": s.Config.DingPushEnable,
|
||||
"ding_robot": s.Config.DingRobot,
|
||||
}).Error
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return "保存成功!"
|
||||
}
|
||||
func (s SettingsApi) GetConfig() *Settings {
|
||||
var settings Settings
|
||||
db.Dao.Model(&Settings{}).First(&settings)
|
||||
return &settings
|
||||
}
|
@ -108,10 +108,10 @@ function toggleFullscreen(e) {
|
||||
:content="content"
|
||||
cross
|
||||
selectable
|
||||
:font-size="12"
|
||||
:line-height="12"
|
||||
:font-size="18"
|
||||
:line-height="18"
|
||||
:height="400"
|
||||
:width="200"
|
||||
:width="600"
|
||||
:x-offset="50"
|
||||
:y-offset="50"
|
||||
:rotate="-15"
|
||||
|
@ -1,26 +1,78 @@
|
||||
<script setup>
|
||||
|
||||
import {ref} from "vue";
|
||||
import {onMounted, ref, watch} from "vue";
|
||||
import {GetConfig, SendDingDingMessageByType, UpdateConfig} from "../../wailsjs/go/main/App";
|
||||
import {useMessage} from "naive-ui";
|
||||
import {data} from "../../wailsjs/go/models";
|
||||
const message = useMessage()
|
||||
|
||||
const formRef = ref(null)
|
||||
const formValue = ref({
|
||||
ID:0,
|
||||
dingPush:{
|
||||
enable:false,
|
||||
dingRobot: 'https://oapi.dingtalk.com/robot/send?access_token=0237527b404598f37ae5d83ef36e936860c7ba5d3892cd43f64c4159d3ed7cb1'
|
||||
dingRobot: ''
|
||||
},
|
||||
localPush:{
|
||||
enable:true,
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
onMounted(()=>{
|
||||
GetConfig().then(res=>{
|
||||
formValue.value.ID = res.ID
|
||||
formValue.value.dingPush = {
|
||||
enable:res.dingPushEnable,
|
||||
dingRobot:res.dingRobot
|
||||
}
|
||||
formValue.value.localPush = {
|
||||
enable:res.localPushEnable,
|
||||
}
|
||||
console.log(res)
|
||||
})
|
||||
//message.info("加载完成")
|
||||
})
|
||||
|
||||
|
||||
function saveConfig(){
|
||||
let config= new data.Settings({
|
||||
ID:formValue.value.ID,
|
||||
dingPushEnable:formValue.value.dingPush.enable,
|
||||
dingRobot:formValue.value.dingPush.dingRobot,
|
||||
localPushEnable:formValue.value.localPush.enable,
|
||||
})
|
||||
|
||||
console.log("Settings",config)
|
||||
UpdateConfig(config).then(res=>{
|
||||
message.success(res)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function getHeight() {
|
||||
return document.documentElement.clientHeight
|
||||
}
|
||||
function sendTestNotice(){
|
||||
let markdown="### go-stock test\n"+new Date()
|
||||
let msg='{' +
|
||||
' "msgtype": "markdown",' +
|
||||
' "markdown": {' +
|
||||
' "title":"go-stock'+new Date()+'",' +
|
||||
' "text": "'+markdown+'"' +
|
||||
' },' +
|
||||
' "at": {' +
|
||||
' "isAtAll": true' +
|
||||
' }' +
|
||||
' }'
|
||||
|
||||
SendDingDingMessageByType(msg, "test-"+new Date().getTime(),1).then(res=>{
|
||||
message.info(res)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-flex justify="start">
|
||||
<n-card title="推送设置">
|
||||
<n-card title="推送设置" style="height: 100%;">
|
||||
<n-form ref="formRef" :model="formValue" :label-placement="'left'" :label-align="'left'">
|
||||
<n-grid :cols="24" :x-gap="24">
|
||||
<n-form-item-gi :span="12" label="是否启用钉钉推送:" path="dingPush.enable" >
|
||||
@ -31,11 +83,18 @@ function getHeight() {
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="24" v-if="formValue.dingPush.enable" label="钉钉机器人接口地址:" path="dingPush.dingRobot" >
|
||||
<n-input placeholder="请输入钉钉机器人接口地址" v-model:value="formValue.dingPush.dingRobot"/>
|
||||
<n-button type="primary" @click="sendTestNotice">发送测试通知</n-button>
|
||||
</n-form-item-gi>
|
||||
<n-gi :span="24">
|
||||
<div style="display: flex; justify-content: flex-end">
|
||||
<n-button round type="primary" @click="saveConfig">
|
||||
保存
|
||||
</n-button>
|
||||
</div>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
</n-form>
|
||||
</n-card>
|
||||
</n-flex>
|
||||
|
||||
</template>
|
||||
|
||||
|
@ -343,7 +343,7 @@ function SendMessage(result,type){
|
||||
"- 成本数量: "+result.costVolume+"股\n" +
|
||||
"- 日期: "+result["日期"]+" "+result["时间"]+"\n\n"+
|
||||
"\n"
|
||||
let title=result["股票名称"]+"("+result["股票代码"]+") "+result["当前价格"]+" "+result.s
|
||||
let title=result["股票名称"]+"("+result["股票代码"]+") "+result["当前价格"]+" "+result.changePercent
|
||||
|
||||
let msg='{' +
|
||||
' "msgtype": "markdown",' +
|
||||
|
4
frontend/wailsjs/go/main/App.d.ts
vendored
4
frontend/wailsjs/go/main/App.d.ts
vendored
@ -4,6 +4,8 @@ import {data} from '../models';
|
||||
|
||||
export function Follow(arg1:string):Promise<string>;
|
||||
|
||||
export function GetConfig():Promise<data.Settings>;
|
||||
|
||||
export function GetFollowList():Promise<Array<data.FollowedStock>>;
|
||||
|
||||
export function GetStockList(arg1:string):Promise<Array<data.StockBasic>>;
|
||||
@ -21,3 +23,5 @@ export function SetCostPriceAndVolume(arg1:string,arg2:number,arg3:number):Promi
|
||||
export function SetStockSort(arg1:number,arg2:string):Promise<void>;
|
||||
|
||||
export function UnFollow(arg1:string):Promise<string>;
|
||||
|
||||
export function UpdateConfig(arg1:data.Settings):Promise<string>;
|
||||
|
@ -6,6 +6,10 @@ export function Follow(arg1) {
|
||||
return window['go']['main']['App']['Follow'](arg1);
|
||||
}
|
||||
|
||||
export function GetConfig() {
|
||||
return window['go']['main']['App']['GetConfig']();
|
||||
}
|
||||
|
||||
export function GetFollowList() {
|
||||
return window['go']['main']['App']['GetFollowList']();
|
||||
}
|
||||
@ -41,3 +45,7 @@ export function SetStockSort(arg1, arg2) {
|
||||
export function UnFollow(arg1) {
|
||||
return window['go']['main']['App']['UnFollow'](arg1);
|
||||
}
|
||||
|
||||
export function UpdateConfig(arg1) {
|
||||
return window['go']['main']['App']['UpdateConfig'](arg1);
|
||||
}
|
||||
|
@ -53,6 +53,51 @@ export namespace data {
|
||||
return a;
|
||||
}
|
||||
}
|
||||
export class Settings {
|
||||
ID: number;
|
||||
// Go type: time
|
||||
CreatedAt: any;
|
||||
// Go type: time
|
||||
UpdatedAt: any;
|
||||
// Go type: gorm
|
||||
DeletedAt: any;
|
||||
localPushEnable: boolean;
|
||||
dingPushEnable: boolean;
|
||||
dingRobot: string;
|
||||
|
||||
static createFrom(source: any = {}) {
|
||||
return new Settings(source);
|
||||
}
|
||||
|
||||
constructor(source: any = {}) {
|
||||
if ('string' === typeof source) source = JSON.parse(source);
|
||||
this.ID = source["ID"];
|
||||
this.CreatedAt = this.convertValues(source["CreatedAt"], null);
|
||||
this.UpdatedAt = this.convertValues(source["UpdatedAt"], null);
|
||||
this.DeletedAt = this.convertValues(source["DeletedAt"], null);
|
||||
this.localPushEnable = source["localPushEnable"];
|
||||
this.dingPushEnable = source["dingPushEnable"];
|
||||
this.dingRobot = source["dingRobot"];
|
||||
}
|
||||
|
||||
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 StockBasic {
|
||||
ID: number;
|
||||
// Go type: time
|
||||
|
3
main.go
3
main.go
@ -40,11 +40,12 @@ func main() {
|
||||
db.Dao.AutoMigrate(&data.StockBasic{})
|
||||
db.Dao.AutoMigrate(&data.FollowedStock{})
|
||||
db.Dao.AutoMigrate(&data.IndexBasic{})
|
||||
db.Dao.AutoMigrate(&data.Settings{})
|
||||
|
||||
if stocksBin != nil && len(stocksBin) > 0 {
|
||||
go initStockData()
|
||||
}
|
||||
updateBasicInfo()
|
||||
//updateBasicInfo()
|
||||
|
||||
// Create an instance of the app structure
|
||||
app := NewApp()
|
||||
|
Loading…
x
Reference in New Issue
Block a user