diff --git a/config-dev.yaml b/config-dev.yaml index edd695b..4362ae9 100644 --- a/config-dev.yaml +++ b/config-dev.yaml @@ -28,6 +28,7 @@ service-account: app-id: wxc236cddde8e7f863 app-secret: 26c1fcecfc98a748d8916355623c975c +# 微信支付 wechat-pay: mch-id: 1735188493 # 商户号 mch-certificate-serial-number: 3725BFCA9CA3AF819AEC5D0CB7D3540BBC67F2CF # 商户证书序列号 @@ -37,6 +38,7 @@ wechat-pay: public-key-path: /Users/blizzard/privateFolder/cert/pub_key.pem # 商户APIv3密钥对应的公钥 notify-url: https://prod.sundynix.cn/api/wechatpay/notify # 微信支付结果通知回调地址 +#百度植物识别 baidu-img-classify: api-key: hpBfjwy8ifv3qswYGYjUCNKN secret-key: i5aXZdM4XZVuDroBslL0f3uIuwbAyXFS diff --git a/initialize/timer.go b/initialize/timer.go index 682bee6..411ceab 100644 --- a/initialize/timer.go +++ b/initialize/timer.go @@ -1,41 +1,28 @@ package initialize -//func InitTimer() { -// go func() { -// var option []cron.Option -// option = append(option, cron.WithSeconds()) -// -// //任务一:每天凌晨00:01点执行 生成今日养护任务 -// _, err := global.Timer.AddTaskByFuncWithSecond("GenerateToday", "0 1 0 * * *", func() { -// err := task.GeneratorTodayCare() -// if err != nil { -// fmt.Println("定时生成今日养护任务失败:", err) -// } -// }, "定时生成今日带养护记录", option...) -// if err != nil { -// fmt.Println("添加定时任务失败:", err) -// } -// -// // 任务二:每天凌晨00:16执行 定时更新未完成的任务 -// _, err2 := global.Timer.AddTaskByFuncWithSecond("UpdateExpireCare", "0 16 0 * * *", func() { -// err3 := task.UpdateExpireCare() -// if err3 != nil { -// fmt.Println("定时更新未完成任务失败:", err) -// } -// }, "定时更新未完成任务", option...) -// if err2 != nil { -// fmt.Println("添加定时任务失败:", err) -// } -// -// // 任务三:每天8点执行 发送植物养护提醒 -// _, err4 := global.Timer.AddTaskByFuncWithSecond("SendCareRemind", "0 0 8 * * *", func() { -// err5 := task.SendCareMsg() -// if err5 != nil { -// global.Logger.Error("定时发送植物养护提醒失败", zap.Error(err5)) -// } -// }, "定时发送植物养护提醒", option...) -// if err4 != nil { -// fmt.Println("添加定时任务失败:", err) -// } -// }() -//} +import ( + "fmt" + "sundynix-go/global" + "sundynix-go/task" + + "github.com/robfig/cron/v3" + "go.uber.org/zap" +) + +func InitTimer() { + go func() { + var option []cron.Option + option = append(option, cron.WithSeconds()) + + // 任务一:每天8点30执行 发送植物养护提醒 + _, err := global.Timer.AddTaskByFuncWithSecond("SendCareRemind", "0 30 8 * * *", func() { + err1 := task.SendCareMsg() + if err1 != nil { + global.Logger.Error("定时发送植物养护提醒失败", zap.Error(err1)) + } + }, "定时发送植物养护提醒", option...) + if err != nil { + fmt.Println("添加定时任务失败:", err) + } + }() +} diff --git a/main.go b/main.go index f884c5a..41ee800 100644 --- a/main.go +++ b/main.go @@ -30,7 +30,7 @@ func main() { initialize.Redis() // timer - //initialize.InitTimer() + initialize.InitTimer() // httpclient 主动初始化 HTTP Client(可选,也可依赖懒加载)饿汉加载 httpclient.InitHttpClient() diff --git a/model/plant/my_classify_record.go b/model/plant/my_classify_record.go index 8a94050..4f2941a 100644 --- a/model/plant/my_classify_record.go +++ b/model/plant/my_classify_record.go @@ -9,8 +9,8 @@ import ( type ClassifyRecord struct { global.BaseModel - UserId string `gorm:"uniqueIndex" json:"userId"` - LogId uint64 `gorm:"uniqueIndex" json:"logId"` + UserId string `gorm:"index;" json:"userId"` + LogId uint64 `gorm:"index;" json:"logId"` AllResults ResultsArray `gorm:"type:json" json:"allResults"` } diff --git a/task/care_message_send_task.go b/task/care_message_send_task.go index 5bb4b60..35aebf5 100644 --- a/task/care_message_send_task.go +++ b/task/care_message_send_task.go @@ -1,117 +1,114 @@ package task -// -//import ( -// "bytes" -// "encoding/json" -// "fmt" -// "io" -// "sundynix-go/global" -// "sundynix-go/model/plant" -// "sundynix-go/model/system" -// "sundynix-go/pkg/httpclient" -// "sundynix-go/utils/wechat" -// "time" -//) -// -//type TemplateDataItem struct { -// Value string `json:"value"` -//} -// -//// SendMessagePayload 是我们发送给微信服务器的消息体 -//type SendMessagePayload struct { -// TemplateID string `json:"template_id"` -// Page string `json:"page"` -// Touser string `json:"touser"` -// Data map[string]TemplateDataItem `json:"data"` -// MiniProgramState string `json:"miniprogram_state"` //跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版 -// Lang string `json:"lang"` -//} -// -//// SendMessageResponse 是发送消息的响应体 -//type SendMessageResponse struct { -// Errcode int `json:"errcode"` -// Errmsg string `json:"errmsg"` -//} -// -//// SendCareMsg 发送提醒消息 -//func SendCareMsg() error { -// httpUrl := "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + wechat.GetMiniAccessToken() -// -// //1.查询出今日的养护任务 -// now := time.Now() -// today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local) -// var tasks []*plant.TodayCare -// err := global.DB.Where("expected_date = ? and status in (1,4)", today).Find(&tasks).Error -// if err != nil { -// return err -// } -// if len(tasks) > 0 { -// // 将tasks分组,key为用户id 保证无论用户有多少植物,只给用户发送一条消息 -// tasksMap := make(map[string][]*plant.TodayCare) -// for _, task := range tasks { -// tasksMap[task.UserId] = append(tasksMap[task.UserId], task) -// } -// for userId, cares := range tasksMap { -// //1.查询用户 -// var user system.User -// err = global.DB.Where("id = ?", userId).First(&user).Error -// if err != nil { -// return err -// } -// if user.MiniOpenId != "" { -// //2.用户该养护的植物 -// plantId := cares[0].PlantId -// var myPlant plant.MyPlant -// err = global.DB.Where("id = ?", plantId).First(&myPlant).Error -// if err != nil { -// return err -// } -// //3.构造请求参数 发送订阅消息 -// payload := SendMessagePayload{ -// TemplateID: "inVOG9qy5NylOivO4Xb9H1db6PQlfv5doNNVhh_3iFE", -// Page: "pages/garden/index", -// Touser: user.MiniOpenId, -// Data: map[string]TemplateDataItem{ -// "thing2": { -// Value: myPlant.Name + "等", -// }, -// "time3": { -// // 今天的九点 -// Value: time.Date(now.Year(), now.Month(), now.Day(), 9, 0, 0, 0, time.Local).Format("2006-01-02"), -// }, -// }, -// MiniProgramState: "formal", -// //MiniProgramState: "trial", -// Lang: "zh_CN", -// } -// payloadBytes, err := json.Marshal(payload) -// if err != nil { -// return err -// } -// myHttpClient := httpclient.GetClient() -// resp, err := myHttpClient.Post(httpUrl, "application/json", bytes.NewBuffer(payloadBytes)) -// if err != nil { -// return err -// } -// defer resp.Body.Close() -// body, err := io.ReadAll(resp.Body) -// if err != nil { -// return fmt.Errorf("读取订阅消息响应失败: %v", err) -// } -// -// var smr SendMessageResponse -// err = json.Unmarshal(body, &smr) -// if err != nil { -// return fmt.Errorf("解析订阅消息响应失败: %v, body: %s", err, string(body)) -// } -// -// if smr.Errcode != 0 { -// return fmt.Errorf("微信服务器返回错误: errcode=%d, errmsg=%s", smr.Errcode, smr.Errmsg) -// } -// global.Logger.Info("订阅消息发送成功!") -// } -// } -// } -// return nil -//} +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "sundynix-go/global" + "sundynix-go/model/plant" + "sundynix-go/model/system" + "sundynix-go/pkg/httpclient" + "sundynix-go/utils/timer" + "sundynix-go/utils/wechat" + "time" +) + +type TemplateDataItem struct { + Value string `json:"value"` +} + +// SendMessagePayload 是我们发送给微信服务器的消息体 +type SendMessagePayload struct { + TemplateID string `json:"template_id"` + Page string `json:"page"` + Touser string `json:"touser"` + Data map[string]TemplateDataItem `json:"data"` + MiniProgramState string `json:"miniprogram_state"` //跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版 + Lang string `json:"lang"` +} + +// SendMessageResponse 是发送消息的响应体 +type SendMessageResponse struct { + Errcode int `json:"errcode"` + Errmsg string `json:"errmsg"` +} + +// SendCareMsg 发送提醒消息 +func SendCareMsg() error { + httpUrl := "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + wechat.GetMiniAccessToken() + //1.查询出今日的养护任务 + now := time.Now() + today := timer.GetZeroTime() + endOfToday := today.Add(24 * time.Hour) + var tasks []*plant.CareTask + err := global.DB.Where("status = 1 and due_date < ?", endOfToday).Order("due_date ASC").Find(&tasks).Error + if len(tasks) > 0 { + // 将tasks分组,key为用户id 保证无论用户有多少植物,只给用户发送一条消息 + tasksMap := make(map[string][]*plant.CareTask) + for _, task := range tasks { + tasksMap[task.UserId] = append(tasksMap[task.UserId], task) + } + for userId, cares := range tasksMap { + //1.查询用户 + var user system.User + err = global.DB.Where("id = ?", userId).First(&user).Error + if err != nil { + return err + } + if user.MiniOpenId != "" { + //2.用户该养护的植物 + plantId := cares[0].PlantId + var myPlant plant.MyPlant + err = global.DB.Where("id = ?", plantId).First(&myPlant).Error + if err != nil { + return err + } + //3.构造请求参数 发送订阅消息 + payload := SendMessagePayload{ + TemplateID: "R7fh3NDpuV8DYqI83HpEQvC8mLJy5xMWFl1qeGN9JIo", + Page: "pages/garden/index", + Touser: user.MiniOpenId, + Data: map[string]TemplateDataItem{ + "thing2": { + Value: myPlant.Name + "等", + }, + "time3": { + // 今天的九点 + Value: time.Date(now.Year(), now.Month(), now.Day(), 8, 30, 0, 0, time.Local).Format("2006-01-02"), + }, + }, + //MiniProgramState: "formal", + MiniProgramState: "trial", + Lang: "zh_CN", + } + payloadBytes, err := json.Marshal(payload) + if err != nil { + return err + } + myHttpClient := httpclient.GetClient() + resp, err := myHttpClient.Post(httpUrl, "application/json", bytes.NewBuffer(payloadBytes)) + if err != nil { + return err + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("读取订阅消息响应失败: %v", err) + } + + var smr SendMessageResponse + err = json.Unmarshal(body, &smr) + if err != nil { + return fmt.Errorf("解析订阅消息响应失败: %v, body: %s", err, string(body)) + } + + if smr.Errcode != 0 { + return fmt.Errorf("微信服务器返回错误: errcode=%d, errmsg=%s", smr.Errcode, smr.Errmsg) + } + global.Logger.Info("订阅消息发送成功!") + } + } + } + return nil +} diff --git a/utils/timer/interval.go b/utils/timer/interval.go index 723fbf2..3951722 100644 --- a/utils/timer/interval.go +++ b/utils/timer/interval.go @@ -31,6 +31,8 @@ func TimeInterval(targetTime time.Time) string { case totalMinutes >= 60: // 1小时 = 60分钟 hours := totalMinutes / 60 return fmt.Sprintf("%d小时前", hours) + case totalMinutes < 1: + return "刚刚" default: // 不足1小时 return fmt.Sprintf("%d分钟前", totalMinutes) } diff --git a/utils/wechat/access_token.go b/utils/wechat/access_token.go index cd6a0cf..6400903 100644 --- a/utils/wechat/access_token.go +++ b/utils/wechat/access_token.go @@ -6,8 +6,8 @@ import ( "errors" "io" "log" - "net/http" "sundynix-go/global" + "sundynix-go/pkg/httpclient" "time" "github.com/redis/go-redis/v9" @@ -20,7 +20,8 @@ func GetMiniAccessToken() string { // 从微信服务器获取 //重新从微信服务器获取 url := "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + global.Config.MiniProgram.AppId + "&secret=" + global.Config.MiniProgram.AppSecret - resp, err := http.Get(url) + myHttpClient := httpclient.GetClient() + resp, err := myHttpClient.Get(url) if err != nil { log.Fatalf("Error making GET request: %s", err) } @@ -46,8 +47,6 @@ func GetMiniAccessToken() string { global.Redis.Set(context.Background(), "mini_access_token", ak, time.Duration(ex)*time.Second) //秒 } else if err != nil { log.Fatalf("Error getting access token from Redis: %s", err) - } else { - return ak } return ak