feat(settings): 添加推送设置功能- 新增本地推送和钉钉推送的配置选项

- 实现配置的保存和读取功能- 添加测试通知按钮
-优化股票信息的显示格式
This commit is contained in:
sparkmemory 2025-01-11 14:16:28 +08:00
parent 7c52cd1d26
commit 9dc8fa97df
12 changed files with 211 additions and 21 deletions

13
app.go
View File

@ -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()
}

View File

@ -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()
}

View File

@ -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,

View File

@ -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 "发送钉钉消息失败"

View 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
}

View File

@ -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"

View File

@ -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>

View File

@ -343,7 +343,7 @@ function SendMessage(result,type){
"- 成本数量: "+result.costVolume+"股\n" +
"- 日期: "+result["日期"]+" "+result["时间"]+"\n\n"+
"![image]("+img+")\n"
let title=result["股票名称"]+"("+result["股票代码"]+") "+result["当前价格"]+" "+result.s
let title=result["股票名称"]+"("+result["股票代码"]+") "+result["当前价格"]+" "+result.changePercent
let msg='{' +
' "msgtype": "markdown",' +

View File

@ -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>;

View File

@ -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);
}

View File

@ -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

View File

@ -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()