diff --git a/.release_log b/.release_log index 60ad585..5b91ab1 100644 --- a/.release_log +++ b/.release_log @@ -19,3 +19,4 @@ 19. 支持微信测试公众号模板消息发送 20. 支持设置自定义的定时消息推送 21. 支持sqlite部署,支持不同版本mysql +22. 增加托管消息,现在可以将站点作为消息的接受,登录站点查看消息 diff --git a/migrate/migrate.go b/migrate/migrate.go index 2d12bf8..9c348cc 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -65,6 +65,7 @@ func Setup() { &models.SendTasksIns{}, &models.Settings{}, &models.CronMessages{}, + &models.HostedMessage{}, } for _, table := range tables { diff --git a/models/hosted_mesages.go b/models/hosted_mesages.go new file mode 100644 index 0000000..50c658e --- /dev/null +++ b/models/hosted_mesages.go @@ -0,0 +1,81 @@ +package models + +import ( + "fmt" + "message-nest/pkg/util" +) + +type HostedMessage struct { + ID int `gorm:"primaryKey" json:"id" ` + Title string `json:"title" gorm:"type:text ;"` + Content string `json:"content" gorm:"type:text ;"` + Type string `json:"type" gorm:"type:varchar(100) ;default:'';index"` + + CreatedAt util.Time `json:"created_on" gorm:"column:created_on;autoCreateTime "` + UpdatedAt util.Time `json:"modified_on" gorm:"column:modified_on;autoUpdateTime ;"` +} + +// Add 添加托管消息 +func (message *HostedMessage) Add() error { + if err := db.Create(&message).Error; err != nil { + return err + } + return nil +} + +// 托管消息的结果 +type HostMessageResult struct { + ID int `json:"id"` + Title string `json:"title"` + Content string `json:"content"` + Type string `json:"type"` + CreatedOn util.Time `json:"created_on"` + ModifiedOn util.Time `json:"modified_on"` +} + +// GetHostMessages 获取所有托管消息记录 +func GetHostMessages(pageNum int, pageSize int, text string, maps map[string]interface{}) ([]HostMessageResult, error) { + var datas []HostMessageResult + hostMessageT := GetSchema(HostedMessage{}) + + query := db.Table(hostMessageT) + + //dayVal, ok := maps["day_created_on"] + //if ok { + // delete(maps, "day_created_on") + // query = query.Where(fmt.Sprintf("DATE(%s.created_on) = ?", logt), dayVal) + //} + + query = query.Where(maps) + if text != "" { + query = query.Where("title like ? or content like ?", fmt.Sprintf("%%%s%%", text), fmt.Sprintf("%%%s%%", text)) + } + query = query.Order("created_on DESC") + if pageSize > 0 || pageNum > 0 { + query = query.Offset(pageNum).Limit(pageSize) + } + query.Scan(&datas) + + return datas, nil +} + +// GetHostMessagesTotal 获取托管消息总数 +func GetHostMessagesTotal(text string, maps map[string]interface{}) (int64, error) { + var total int64 + hostMessageT := GetSchema(HostedMessage{}) + + query := db.Table(hostMessageT) + + //dayVal, ok := maps["day_created_on"] + //if ok { + // delete(maps, "day_created_on") + // query = query.Where(fmt.Sprintf("DATE(%s.created_on) = ?", logt), dayVal) + //} + + query = query.Where(maps) + if text != "" { + query = query.Where("title like ? or content like ?", fmt.Sprintf("%%%s%%", text), fmt.Sprintf("%%%s%%", text)) + } + query.Count(&total) + return total, nil +} diff --git a/models/send_ins.go b/models/send_ins.go index dff0219..c8049d3 100644 --- a/models/send_ins.go +++ b/models/send_ins.go @@ -34,6 +34,10 @@ type InsQyWeiXinConfig struct { type InsCustomConfig struct { } +// InsMessageNestConfig 实例里面的托管消息config +type InsMessageNestConfig struct { +} + // ManyAddTaskIns 批量添加实例 func ManyAddTaskIns(taskIns []SendTasksIns) error { tx := db.Begin() diff --git a/models/send_tasks_logs.go b/models/send_tasks_logs.go index bce4f51..a26a27b 100644 --- a/models/send_tasks_logs.go +++ b/models/send_tasks_logs.go @@ -119,10 +119,11 @@ func DeleteOutDateLogs(keepNum int) (int, error) { } type StatisticData struct { - TodaySuccNum int `json:"today_succ_num"` - TodayFailedNum int `json:"today_failed_num"` - TodayTotalNum int `json:"today_total_num"` - MessageTotalNum int `json:"message_total_num"` + TodaySuccNum int `json:"today_succ_num"` + TodayFailedNum int `json:"today_failed_num"` + TodayTotalNum int `json:"today_total_num"` + MessageTotalNum int `json:"message_total_num"` + HostedMessageTotalNum int `json:"hosted_message_total_num"` LatestSendData []LatestSendData `json:"latest_send_data" gorm:"many2many:latest_send_data;"` WayCateData []WayCateData `json:"way_cate_data" gorm:"many2many:way_cate_data;"` @@ -148,6 +149,7 @@ func GetStatisticData() (StatisticData, error) { var wayCateData []WayCateData logt := GetSchema(SendTasksLogs{}) inst := GetSchema(SendTasksIns{}) + hostedt := GetSchema(HostedMessage{}) wayst := GetSchema(SendWays{}) currDay := util.GetNowTimeStr()[:10] @@ -166,6 +168,10 @@ func GetStatisticData() (StatisticData, error) { totalQuery := db.Table(logt).Select(`COUNT(*) AS message_total_num`) totalQuery.Take(&statistic) + // 托管消息统计数据 + hostedMessageTotalQuery := db.Table(hostedt).Select(`COUNT(*) AS hosted_message_total_num`) + hostedMessageTotalQuery.Take(&statistic) + // 最近30天数据 days := 30 now := util.GetNowTime() diff --git a/routers/api/v1/hosted_message.go b/routers/api/v1/hosted_message.go new file mode 100644 index 0000000..fe8808d --- /dev/null +++ b/routers/api/v1/hosted_message.go @@ -0,0 +1,38 @@ +package v1 + +import ( + "github.com/gin-gonic/gin" + "message-nest/pkg/app" + "message-nest/pkg/util" + "message-nest/service/hosted_message_service" + "net/http" +) + +// GetHostMessageList 获取托管消息列表 +func GetHostMessageList(c *gin.Context) { + appG := app.Gin{C: c} + text := c.Query("text") + + offset, limit := util.GetPageSize(c) + messageService := hosted_message_service.HostMessageService{ + Text: text, + PageNum: offset, + PageSize: limit, + } + ways, err := messageService.GetAll() + if err != nil { + appG.CResponse(http.StatusInternalServerError, "获取托管消息失败!", nil) + return + } + + count, err := messageService.Count() + if err != nil { + appG.CResponse(http.StatusInternalServerError, "获取托管消息总数失败!", nil) + return + } + + appG.CResponse(http.StatusOK, "获取托管消息成功", map[string]interface{}{ + "lists": ways, + "total": count, + }) +} diff --git a/routers/api/v1/send_ways.go b/routers/api/v1/send_ways.go index 0e5ef13..d775150 100644 --- a/routers/api/v1/send_ways.go +++ b/routers/api/v1/send_ways.go @@ -97,7 +97,7 @@ func GetMsgSendWayList(c *gin.Context) { type AddMsgSendWayReq struct { Name string `json:"name" validate:"required,max=100,min=1" label:"渠道名"` Type string `json:"type" validate:"required,max=100,min=1" label:"渠道类型"` - Auth string `json:"auth" validate:"required,max=2048,min=6" label:"渠道认证方式"` + Auth string `json:"auth" label:"渠道认证方式"` } // AddMsgSendWay 添加发送渠道 diff --git a/routers/router.go b/routers/router.go index 11a01ee..355f033 100644 --- a/routers/router.go +++ b/routers/router.go @@ -91,6 +91,9 @@ func InitRouter(f embed.FS) *gin.Engine { apiV1.POST("/cronmessages/delete", v1.DeleteCronMsgTask) apiV1.POST("/cronmessages/edit", v1.EditCronMsgTask) + // hostedMessage + apiV1.GET("/hostedmessages/list", v1.GetHostMessageList) + } return app diff --git a/service/hosted_message_service/hosted_message.go b/service/hosted_message_service/hosted_message.go new file mode 100644 index 0000000..66a4067 --- /dev/null +++ b/service/hosted_message_service/hosted_message.go @@ -0,0 +1,54 @@ +package hosted_message_service + +import ( + "message-nest/models" +) + +type HostMessageService struct { + ID int + Text string + Title string + Type string + Content string + + PageNum int + PageSize int +} + +func (st *HostMessageService) Add() error { + model := models.HostedMessage{ + Title: st.Title, + Content: st.Content, + Type: st.Type, + } + return model.Add() +} + +func (st *HostMessageService) Count() (int64, error) { + return models.GetHostMessagesTotal(st.Text, st.getMaps()) +} + +func (st *HostMessageService) GetAll() ([]models.HostMessageResult, error) { + tasks, err := models.GetHostMessages(st.PageNum, st.PageSize, st.Text, st.getMaps()) + if err != nil { + return nil, err + } + return tasks, nil +} + +func (st *HostMessageService) getMaps() map[string]interface{} { + maps := make(map[string]interface{}) + //if len(st.Query) > 0 { + // decodedString, err := url.QueryUnescape(st.Query) + // if err != nil { + // logrus.Errorf("queryUrl编码解码失败: %s", err) + // return maps + // } + // err = json.Unmarshal([]byte(decodedString), &maps) + // if err != nil { + // logrus.Errorf("queryJson反序列化失败: %s", err) + // return maps + // } + //} + return maps +} diff --git a/service/send_ins_service/send_ins.go b/service/send_ins_service/send_ins.go index 636dd45..0973fe9 100644 --- a/service/send_ins_service/send_ins.go +++ b/service/send_ins_service/send_ins.go @@ -39,6 +39,10 @@ func (sw *SendTaskInsService) ValidateDiffIns(ins models.SendTasksIns) (string, var Config models.InsQyWeiXinConfig return "", Config } + if ins.WayType == "MessageNest" { + var Config models.InsQyWeiXinConfig + return "", Config + } if ins.WayType == "Custom" { var Config models.InsCustomConfig err := json.Unmarshal([]byte(ins.Config), &Config) diff --git a/service/send_message_service/send_hosted_message.go b/service/send_message_service/send_hosted_message.go new file mode 100644 index 0000000..7c670ae --- /dev/null +++ b/service/send_message_service/send_hosted_message.go @@ -0,0 +1,36 @@ +package send_message_service + +import ( + "message-nest/models" + "message-nest/service/hosted_message_service" + "message-nest/service/send_way_service" +) + +type HostMessageService struct { +} + +// SendHostMessage 执行托管消息记录 +func (s *HostMessageService) SendHostMessage( + auth send_way_service.MessageNest, + ins models.SendTasksIns, + typeC string, + title string, + content string) (string, string) { + + errMsg := "" + var res string + var err error + messageService := hosted_message_service.HostMessageService{ + Title: title, + Content: content, + Type: typeC, + } + err = messageService.Add() + if err != nil { + errMsg = err.Error() + res = "托管消息创建失败!" + } else { + res = "托管消息创建成功!" + } + return string(res), errMsg +} diff --git a/service/send_message_service/send_message.go b/service/send_message_service/send_message.go index ae93c2e..7c9a2e0 100644 --- a/service/send_message_service/send_message.go +++ b/service/send_message_service/send_message.go @@ -194,6 +194,15 @@ func (sm *SendMessageService) Send(task models.TaskIns) (string, error) { sm.LogsAndStatusMark(sm.TransError(errMsg), errStrIsSuccess(errMsg)) continue } + // 托管消息的实例发送 + mnt, ok := msgObj.(send_way_service.MessageNest) + if ok { + cs := HostMessageService{} + res, errMsg := cs.SendHostMessage(mnt, ins.SendTasksIns, typeC, sm.Title, content) + sm.LogsAndStatusMark(fmt.Sprintf("返回内容:%s", res), sm.Status) + sm.LogsAndStatusMark(sm.TransError(errMsg), errStrIsSuccess(errMsg)) + continue + } sm.LogsAndStatusMark(fmt.Sprintf("发送失败:未知渠道的发信实例: %s\n", ins.ID), SendFail) } diff --git a/service/send_way_service/send_way.go b/service/send_way_service/send_way.go index 20a4ead..6b0a7b4 100644 --- a/service/send_way_service/send_way.go +++ b/service/send_way_service/send_way.go @@ -56,6 +56,10 @@ type WeChatOFAccount struct { TempID string `json:"tempid" validate:"max=2000" label:"模板消息id"` } +// MessageNest 自托管消息 +type MessageNest struct { +} + func (sw *SendWay) GetByID() (interface{}, error) { return models.GetWayByID(sw.ID) } @@ -170,6 +174,14 @@ func (sw *SendWay) ValidateDiffWay() (string, interface{}) { } _, Msg := app.CommonPlaygroundValid(wca) return Msg, wca + } else if sw.Type == "MessageNest" { + var wca MessageNest + err := json.Unmarshal([]byte(sw.Auth), &wca) + if err != nil { + return "自托管消息反序列化失败!", empty + } + _, Msg := app.CommonPlaygroundValid(wca) + return Msg, wca } return fmt.Sprintf("未知的发信渠道校验: %s", sw.Type), empty } @@ -209,11 +221,11 @@ func (sw *SendWay) TestSendWay(msgObj interface{}) (string, string) { } _, ok = msgObj.(WeChatOFAccount) if ok { - //var cli = message.WeChatOFAccount{ - // AppID: wca.AppID, - // AppSecret: wca.APPSecret, - //} return "微信公众号模板消息不用测试运行,请直接添加", "" } + _, ok = msgObj.(MessageNest) + if ok { + return "自托管消息不用测试运行,请直接添加", "" + } return fmt.Sprintf("未知的发信渠道校验: %s", sw.Type), "" } diff --git a/web/src/constant.js b/web/src/constant.js index b713192..2eee257 100644 --- a/web/src/constant.js +++ b/web/src/constant.js @@ -69,7 +69,7 @@ const CONSTANT = { }, { type: 'Custom', - label: '自定义', + label: '自定义推送', inputs: [ { subLabel: 'webhook地址', value: '', col: 'webhook', desc: "自定义webhook地址" }, { subLabel: '请求体', value: '', col: 'body', desc: "请求体, text内容请使用 TEXT 进行占位\n例如:{\"message\": \"TEXT\", \"foo\": \"bar\"}", isTextArea: true }, @@ -103,6 +103,21 @@ const CONSTANT = { { value: '', col: 'to_account', desc: "要发送的OpenId(登录微信公众号后台查看)" }, ], }, + { + type: 'MessageNest', + label: '自托管消息', + inputs: [ + { subLabel: '渠道名', value: '', col: 'name', desc: "想要设置的渠道名字" }, + ], + tips: { + text: "自托管消息说明", desc: "站点本身会作为消息接收站点,接收、展示推送过来的消息。" + }, + taskInsRadios: [ + { subLabel: 'text', content: 'text' }, + ], + taskInsInputs: [ + ], + }, ], API_VIEW_DATA: [ { label: "curl", class: "language-shell line-numbers", code: "", func: ApiStrGenerate.getCurlString }, diff --git a/web/src/router/index.js b/web/src/router/index.js index f56dc25..9cb08fc 100644 --- a/web/src/router/index.js +++ b/web/src/router/index.js @@ -41,6 +41,11 @@ const router = createRouter({ path: '/settings', name: 'settings', component: () => import('../views/tabsTools/settings/settings.vue') + }, + { + path: '/hostedMessage', + name: 'hostedmessage', + component: () => import('../views/tabsTools/hostedMessage/hostedMessage.vue') }, { path: '/:catchAll(.*)', diff --git a/web/src/views/home/index.vue b/web/src/views/home/index.vue index 97eb187..f1b0c33 100644 --- a/web/src/views/home/index.vue +++ b/web/src/views/home/index.vue @@ -35,10 +35,11 @@ export default { const menuData = reactive([ { id: '0', title: '数据统计', path: '/statistic' }, { id: '1', title: '发信日志', path: '/sendlogs' }, - { id: '2', title: '定时发信', path: '/cronmessages' }, - { id: '3', title: '发信任务', path: '/sendtasks' }, - { id: '4', title: '发信渠道', path: '/sendways' }, - { id: '5', title: '设置', path: '/settings' }, + { id: '2', title: '托管消息', path: '/hostedmessage' }, + { id: '3', title: '定时发信', path: '/cronmessages' }, + { id: '4', title: '发信任务', path: '/sendtasks' }, + { id: '5', title: '发信渠道', path: '/sendways' }, + { id: '6', title: '设置', path: '/settings' }, ]); const checkIsLogin = () => { diff --git a/web/src/views/tabsTools/hostedMessage/hostedMessage.vue b/web/src/views/tabsTools/hostedMessage/hostedMessage.vue new file mode 100644 index 0000000..d6c80bb --- /dev/null +++ b/web/src/views/tabsTools/hostedMessage/hostedMessage.vue @@ -0,0 +1,206 @@ + + + + + + \ No newline at end of file diff --git a/web/src/views/tabsTools/statistic/statistic.vue b/web/src/views/tabsTools/statistic/statistic.vue index a39d187..8d08bf8 100644 --- a/web/src/views/tabsTools/statistic/statistic.vue +++ b/web/src/views/tabsTools/statistic/statistic.vue @@ -1,8 +1,8 @@