feat: add hosted message
This commit is contained in:
@@ -19,3 +19,4 @@
|
|||||||
19. 支持微信测试公众号模板消息发送
|
19. 支持微信测试公众号模板消息发送
|
||||||
20. 支持设置自定义的定时消息推送
|
20. 支持设置自定义的定时消息推送
|
||||||
21. 支持sqlite部署,支持不同版本mysql
|
21. 支持sqlite部署,支持不同版本mysql
|
||||||
|
22. 增加托管消息,现在可以将站点作为消息的接受,登录站点查看消息
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ func Setup() {
|
|||||||
&models.SendTasksIns{},
|
&models.SendTasksIns{},
|
||||||
&models.Settings{},
|
&models.Settings{},
|
||||||
&models.CronMessages{},
|
&models.CronMessages{},
|
||||||
|
&models.HostedMessage{},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, table := range tables {
|
for _, table := range tables {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -34,6 +34,10 @@ type InsQyWeiXinConfig struct {
|
|||||||
type InsCustomConfig struct {
|
type InsCustomConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InsMessageNestConfig 实例里面的托管消息config
|
||||||
|
type InsMessageNestConfig struct {
|
||||||
|
}
|
||||||
|
|
||||||
// ManyAddTaskIns 批量添加实例
|
// ManyAddTaskIns 批量添加实例
|
||||||
func ManyAddTaskIns(taskIns []SendTasksIns) error {
|
func ManyAddTaskIns(taskIns []SendTasksIns) error {
|
||||||
tx := db.Begin()
|
tx := db.Begin()
|
||||||
|
|||||||
@@ -119,10 +119,11 @@ func DeleteOutDateLogs(keepNum int) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type StatisticData struct {
|
type StatisticData struct {
|
||||||
TodaySuccNum int `json:"today_succ_num"`
|
TodaySuccNum int `json:"today_succ_num"`
|
||||||
TodayFailedNum int `json:"today_failed_num"`
|
TodayFailedNum int `json:"today_failed_num"`
|
||||||
TodayTotalNum int `json:"today_total_num"`
|
TodayTotalNum int `json:"today_total_num"`
|
||||||
MessageTotalNum int `json:"message_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;"`
|
LatestSendData []LatestSendData `json:"latest_send_data" gorm:"many2many:latest_send_data;"`
|
||||||
WayCateData []WayCateData `json:"way_cate_data" gorm:"many2many:way_cate_data;"`
|
WayCateData []WayCateData `json:"way_cate_data" gorm:"many2many:way_cate_data;"`
|
||||||
@@ -148,6 +149,7 @@ func GetStatisticData() (StatisticData, error) {
|
|||||||
var wayCateData []WayCateData
|
var wayCateData []WayCateData
|
||||||
logt := GetSchema(SendTasksLogs{})
|
logt := GetSchema(SendTasksLogs{})
|
||||||
inst := GetSchema(SendTasksIns{})
|
inst := GetSchema(SendTasksIns{})
|
||||||
|
hostedt := GetSchema(HostedMessage{})
|
||||||
wayst := GetSchema(SendWays{})
|
wayst := GetSchema(SendWays{})
|
||||||
currDay := util.GetNowTimeStr()[:10]
|
currDay := util.GetNowTimeStr()[:10]
|
||||||
|
|
||||||
@@ -166,6 +168,10 @@ func GetStatisticData() (StatisticData, error) {
|
|||||||
totalQuery := db.Table(logt).Select(`COUNT(*) AS message_total_num`)
|
totalQuery := db.Table(logt).Select(`COUNT(*) AS message_total_num`)
|
||||||
totalQuery.Take(&statistic)
|
totalQuery.Take(&statistic)
|
||||||
|
|
||||||
|
// 托管消息统计数据
|
||||||
|
hostedMessageTotalQuery := db.Table(hostedt).Select(`COUNT(*) AS hosted_message_total_num`)
|
||||||
|
hostedMessageTotalQuery.Take(&statistic)
|
||||||
|
|
||||||
// 最近30天数据
|
// 最近30天数据
|
||||||
days := 30
|
days := 30
|
||||||
now := util.GetNowTime()
|
now := util.GetNowTime()
|
||||||
|
|||||||
@@ -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,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -97,7 +97,7 @@ func GetMsgSendWayList(c *gin.Context) {
|
|||||||
type AddMsgSendWayReq struct {
|
type AddMsgSendWayReq struct {
|
||||||
Name string `json:"name" validate:"required,max=100,min=1" label:"渠道名"`
|
Name string `json:"name" validate:"required,max=100,min=1" label:"渠道名"`
|
||||||
Type string `json:"type" 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 添加发送渠道
|
// AddMsgSendWay 添加发送渠道
|
||||||
|
|||||||
@@ -91,6 +91,9 @@ func InitRouter(f embed.FS) *gin.Engine {
|
|||||||
apiV1.POST("/cronmessages/delete", v1.DeleteCronMsgTask)
|
apiV1.POST("/cronmessages/delete", v1.DeleteCronMsgTask)
|
||||||
apiV1.POST("/cronmessages/edit", v1.EditCronMsgTask)
|
apiV1.POST("/cronmessages/edit", v1.EditCronMsgTask)
|
||||||
|
|
||||||
|
// hostedMessage
|
||||||
|
apiV1.GET("/hostedmessages/list", v1.GetHostMessageList)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -39,6 +39,10 @@ func (sw *SendTaskInsService) ValidateDiffIns(ins models.SendTasksIns) (string,
|
|||||||
var Config models.InsQyWeiXinConfig
|
var Config models.InsQyWeiXinConfig
|
||||||
return "", Config
|
return "", Config
|
||||||
}
|
}
|
||||||
|
if ins.WayType == "MessageNest" {
|
||||||
|
var Config models.InsQyWeiXinConfig
|
||||||
|
return "", Config
|
||||||
|
}
|
||||||
if ins.WayType == "Custom" {
|
if ins.WayType == "Custom" {
|
||||||
var Config models.InsCustomConfig
|
var Config models.InsCustomConfig
|
||||||
err := json.Unmarshal([]byte(ins.Config), &Config)
|
err := json.Unmarshal([]byte(ins.Config), &Config)
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -194,6 +194,15 @@ func (sm *SendMessageService) Send(task models.TaskIns) (string, error) {
|
|||||||
sm.LogsAndStatusMark(sm.TransError(errMsg), errStrIsSuccess(errMsg))
|
sm.LogsAndStatusMark(sm.TransError(errMsg), errStrIsSuccess(errMsg))
|
||||||
continue
|
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)
|
sm.LogsAndStatusMark(fmt.Sprintf("发送失败:未知渠道的发信实例: %s\n", ins.ID), SendFail)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ type WeChatOFAccount struct {
|
|||||||
TempID string `json:"tempid" validate:"max=2000" label:"模板消息id"`
|
TempID string `json:"tempid" validate:"max=2000" label:"模板消息id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MessageNest 自托管消息
|
||||||
|
type MessageNest struct {
|
||||||
|
}
|
||||||
|
|
||||||
func (sw *SendWay) GetByID() (interface{}, error) {
|
func (sw *SendWay) GetByID() (interface{}, error) {
|
||||||
return models.GetWayByID(sw.ID)
|
return models.GetWayByID(sw.ID)
|
||||||
}
|
}
|
||||||
@@ -170,6 +174,14 @@ func (sw *SendWay) ValidateDiffWay() (string, interface{}) {
|
|||||||
}
|
}
|
||||||
_, Msg := app.CommonPlaygroundValid(wca)
|
_, Msg := app.CommonPlaygroundValid(wca)
|
||||||
return Msg, 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
|
return fmt.Sprintf("未知的发信渠道校验: %s", sw.Type), empty
|
||||||
}
|
}
|
||||||
@@ -209,11 +221,11 @@ func (sw *SendWay) TestSendWay(msgObj interface{}) (string, string) {
|
|||||||
}
|
}
|
||||||
_, ok = msgObj.(WeChatOFAccount)
|
_, ok = msgObj.(WeChatOFAccount)
|
||||||
if ok {
|
if ok {
|
||||||
//var cli = message.WeChatOFAccount{
|
|
||||||
// AppID: wca.AppID,
|
|
||||||
// AppSecret: wca.APPSecret,
|
|
||||||
//}
|
|
||||||
return "微信公众号模板消息不用测试运行,请直接添加", ""
|
return "微信公众号模板消息不用测试运行,请直接添加", ""
|
||||||
}
|
}
|
||||||
|
_, ok = msgObj.(MessageNest)
|
||||||
|
if ok {
|
||||||
|
return "自托管消息不用测试运行,请直接添加", ""
|
||||||
|
}
|
||||||
return fmt.Sprintf("未知的发信渠道校验: %s", sw.Type), ""
|
return fmt.Sprintf("未知的发信渠道校验: %s", sw.Type), ""
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-1
@@ -69,7 +69,7 @@ const CONSTANT = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Custom',
|
type: 'Custom',
|
||||||
label: '自定义',
|
label: '自定义推送',
|
||||||
inputs: [
|
inputs: [
|
||||||
{ subLabel: 'webhook地址', value: '', col: 'webhook', desc: "自定义webhook地址" },
|
{ subLabel: 'webhook地址', value: '', col: 'webhook', desc: "自定义webhook地址" },
|
||||||
{ subLabel: '请求体', value: '', col: 'body', desc: "请求体, text内容请使用 TEXT 进行占位\n例如:{\"message\": \"TEXT\", \"foo\": \"bar\"}", isTextArea: true },
|
{ 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(登录微信公众号后台查看)" },
|
{ 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: [
|
API_VIEW_DATA: [
|
||||||
{ label: "curl", class: "language-shell line-numbers", code: "", func: ApiStrGenerate.getCurlString },
|
{ label: "curl", class: "language-shell line-numbers", code: "", func: ApiStrGenerate.getCurlString },
|
||||||
|
|||||||
@@ -41,6 +41,11 @@ const router = createRouter({
|
|||||||
path: '/settings',
|
path: '/settings',
|
||||||
name: 'settings',
|
name: 'settings',
|
||||||
component: () => import('../views/tabsTools/settings/settings.vue')
|
component: () => import('../views/tabsTools/settings/settings.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/hostedMessage',
|
||||||
|
name: 'hostedmessage',
|
||||||
|
component: () => import('../views/tabsTools/hostedMessage/hostedMessage.vue')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/:catchAll(.*)',
|
path: '/:catchAll(.*)',
|
||||||
|
|||||||
@@ -35,10 +35,11 @@ export default {
|
|||||||
const menuData = reactive([
|
const menuData = reactive([
|
||||||
{ id: '0', title: '数据统计', path: '/statistic' },
|
{ id: '0', title: '数据统计', path: '/statistic' },
|
||||||
{ id: '1', title: '发信日志', path: '/sendlogs' },
|
{ id: '1', title: '发信日志', path: '/sendlogs' },
|
||||||
{ id: '2', title: '定时发信', path: '/cronmessages' },
|
{ id: '2', title: '托管消息', path: '/hostedmessage' },
|
||||||
{ id: '3', title: '发信任务', path: '/sendtasks' },
|
{ id: '3', title: '定时发信', path: '/cronmessages' },
|
||||||
{ id: '4', title: '发信渠道', path: '/sendways' },
|
{ id: '4', title: '发信任务', path: '/sendtasks' },
|
||||||
{ id: '5', title: '设置', path: '/settings' },
|
{ id: '5', title: '发信渠道', path: '/sendways' },
|
||||||
|
{ id: '6', title: '设置', path: '/settings' },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const checkIsLogin = () => {
|
const checkIsLogin = () => {
|
||||||
|
|||||||
@@ -0,0 +1,206 @@
|
|||||||
|
<template>
|
||||||
|
<div class="main-center-body">
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
<div class="search-input-sendways">
|
||||||
|
<el-input v-model="search" size="small" placeholder="根据关键字搜索相应托管消息" @change="filterFunc()" />
|
||||||
|
</div>
|
||||||
|
<div class="search-box">
|
||||||
|
<el-text class="refresh-time" v-if="refreshText" size="small">{{ refreshText }}</el-text>
|
||||||
|
<el-input-number class="refresh-box" v-model="refreshSec" size="small" :step="2" :min="5"
|
||||||
|
controls-position="right" />
|
||||||
|
<el-switch v-model="refreshSwitch" class="ml-2" width="80" inline-prompt active-text="自动刷新" inactive-text="自动刷新"
|
||||||
|
@click="clickFreshSwitch" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
<div ref="refContainer">
|
||||||
|
<el-table :data="tableData" stripe empty-text="托管消息为空" :row-style="rowStyle()">
|
||||||
|
<el-table-column label="ID" prop="id" width="85px" />
|
||||||
|
<el-table-column label="消息标题" prop="title" show-overflow-tooltip width="150px" />
|
||||||
|
<el-table-column label="托管消息" prop="content">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tooltip enterable placement="top">
|
||||||
|
<template #content>
|
||||||
|
<div v-html="formatLogDisplayHtml(scope)"></div>
|
||||||
|
</template>
|
||||||
|
<span class="log-overflow">{{ scope.row.content }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="发送时间" prop="created_on" width="160px" />
|
||||||
|
<el-table-column label="详情" prop="status" width="120px" fixed="right">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link size="small" style="margin-right: 10px;" type="primary"
|
||||||
|
@click="drawer = true; logText = formatLogDisplayHtml(scope)">查看消息</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-drawer v-model="drawer" :with-header="false">
|
||||||
|
<el-text v-html="logText" size="small"></el-text>
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
|
<div class="pagination-block">
|
||||||
|
<el-pagination layout="prev, pager, next" :total="total" :page-size="pageSize"
|
||||||
|
@current-change="handPageChange" />
|
||||||
|
<el-text class="total-tip" size="small">每页{{ pageSize }}条,共{{ total }}条</el-text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { reactive, toRefs, onMounted, watch, ref } from 'vue'
|
||||||
|
import { request } from '../../../api/api'
|
||||||
|
import { copyToClipboard } from '../../../util/clipboard.js';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { CONSTANT } from '@/constant'
|
||||||
|
import { usePageState } from '@/store/page_sate.js';
|
||||||
|
import { CommonUtils } from "@/util/commonUtils.js";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
},
|
||||||
|
setup() {
|
||||||
|
const pageState = usePageState();
|
||||||
|
const router = useRoute();
|
||||||
|
const state = reactive({
|
||||||
|
search: '',
|
||||||
|
refreshText: '',
|
||||||
|
refreshSwitch: false,
|
||||||
|
refreshSec: 20,
|
||||||
|
refreshIntervalFuncList: [],
|
||||||
|
optionValue: '',
|
||||||
|
logText: '',
|
||||||
|
drawer: false,
|
||||||
|
tableData: [],
|
||||||
|
total: CONSTANT.TOTAL,
|
||||||
|
pageSize: pageState.siteConfigData.pagesize,
|
||||||
|
currPage: CONSTANT.PAGE,
|
||||||
|
});
|
||||||
|
|
||||||
|
const TransHtml = (raw) => {
|
||||||
|
if (raw) {
|
||||||
|
return raw.replace(/\n/g, '<br />')
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatLogDisplayHtml = (scope) => {
|
||||||
|
let content = TransHtml(scope.row.content);
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handPageChange = async (pageNum) => {
|
||||||
|
state.currPage = pageNum;
|
||||||
|
await queryListData(pageNum, state.pageSize);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowStyle = () => {
|
||||||
|
return {
|
||||||
|
'font-size': '13px',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const clickFreshSwitch = () => {
|
||||||
|
if (state.refreshSwitch) {
|
||||||
|
let flag = setInterval(async function () {
|
||||||
|
await filterFunc();
|
||||||
|
state.refreshText = `自动刷新于:${CommonUtils.getCurrentTimeStr()}`;
|
||||||
|
}, state.refreshSec * 1000);
|
||||||
|
state.refreshIntervalFuncList.push(flag);
|
||||||
|
} else {
|
||||||
|
state.refreshIntervalFuncList.forEach(intervalId => {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
});
|
||||||
|
state.refreshIntervalFuncList = [];
|
||||||
|
state.refreshText = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterFunc = async () => {
|
||||||
|
await queryListData(state.currPage, state.pageSize, state.search, state.optionValue);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryListData = async (page, size, name = '', query = '') => {
|
||||||
|
let params = { page: page, size: size, text: name, query: query };
|
||||||
|
const rsp = await request.get('/hostedmessages/list', { params: params });
|
||||||
|
state.tableData = await rsp.data.data.lists;
|
||||||
|
state.total = await rsp.data.data.total;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
state.search = router.query.name;
|
||||||
|
await queryListData(1, state.pageSize, router.query.name, router.query.taskid, router.query.query);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state), TransHtml, clickFreshSwitch,
|
||||||
|
rowStyle, handPageChange, filterFunc, copyToClipboard, formatLogDisplayHtml
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
hr {
|
||||||
|
color: #FAFCFF;
|
||||||
|
background-color: #FAFCFF;
|
||||||
|
border-color: #FAFCFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background-color: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
max-width: 1000px;
|
||||||
|
width: 100%;
|
||||||
|
/* margin-top: -10vh; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
float: right;
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-box {
|
||||||
|
width: 80px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.refresh-time {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination-block {
|
||||||
|
margin-top: 15px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.total-tip {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input-sendways {
|
||||||
|
width: 200px;
|
||||||
|
display: inline-flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.log-overflow {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="main-center-body">
|
<div class="main-center-body">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<el-row :gutter="16">
|
<el-row :gutter="18">
|
||||||
<el-col :span="6">
|
<el-col :span="4">
|
||||||
<div class="statistic-card">
|
<div class="statistic-card">
|
||||||
<el-statistic :value="data.message_total_num">
|
<el-statistic :value="data.message_total_num">
|
||||||
<template #title>
|
<template #title>
|
||||||
@@ -13,7 +13,18 @@
|
|||||||
</el-statistic>
|
</el-statistic>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="5">
|
||||||
|
<div class="statistic-card">
|
||||||
|
<el-statistic :value="data.hosted_message_total_num">
|
||||||
|
<template #title>
|
||||||
|
<div style="display: inline-flex; align-items: center">
|
||||||
|
托管消息数
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-statistic>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="5">
|
||||||
<div class="statistic-card">
|
<div class="statistic-card">
|
||||||
<el-statistic :value="data.today_total_num">
|
<el-statistic :value="data.today_total_num">
|
||||||
<template #title>
|
<template #title>
|
||||||
@@ -24,7 +35,7 @@
|
|||||||
</el-statistic>
|
</el-statistic>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="5">
|
||||||
<div class="statistic-card">
|
<div class="statistic-card">
|
||||||
<el-statistic :value="data.today_succ_num">
|
<el-statistic :value="data.today_succ_num">
|
||||||
<template #title>
|
<template #title>
|
||||||
@@ -35,7 +46,7 @@
|
|||||||
</el-statistic>
|
</el-statistic>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="6">
|
<el-col :span="5">
|
||||||
<div class="statistic-card">
|
<div class="statistic-card">
|
||||||
<el-statistic :value="data.today_failed_num" :value-style="formatFailedNumStyle()">
|
<el-statistic :value="data.today_failed_num" :value-style="formatFailedNumStyle()">
|
||||||
<template #title>
|
<template #title>
|
||||||
|
|||||||
Reference in New Issue
Block a user