feat: add send_stats table
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"message-nest/pkg/util"
|
||||
)
|
||||
|
||||
// SendStats 发送统计表
|
||||
type SendStats struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
TaskID *uint `json:"task_id" gorm:"type:bigint unsigned;index:idx_task_type_day_status"`
|
||||
TaskType string `json:"task_type" gorm:"type:varchar(20);default:'task';index:idx_task_type_day_status;comment:'任务类型:task-发信任务,template-模板任务'"`
|
||||
Day string `json:"day" gorm:"type:varchar(10);index:idx_task_type_day_status;index:idx_day_status"`
|
||||
Status string `json:"status" gorm:"type:varchar(20);index:idx_task_type_day_status;index:idx_day_status"`
|
||||
Num int64 `json:"num" gorm:"type:bigint;default:0"`
|
||||
}
|
||||
|
||||
// SendStatsData 首页统计数据结构
|
||||
type SendStatsData struct {
|
||||
TodaySuccNum int64 `json:"today_succ_num"`
|
||||
TodayFailedNum int64 `json:"today_failed_num"`
|
||||
TodayTotalNum int64 `json:"today_total_num"`
|
||||
TotalSuccNum int64 `json:"total_succ_num"`
|
||||
TotalFailedNum int64 `json:"total_failed_num"`
|
||||
TotalNum int64 `json:"total_num"`
|
||||
DailyStats []DailyStatsData `json:"daily_stats"`
|
||||
StatusStats []StatusStatsData `json:"status_stats"`
|
||||
TaskTypeStats []TaskTypeStatsData `json:"task_type_stats"`
|
||||
}
|
||||
|
||||
// DailyStatsData 每日统计数据
|
||||
type DailyStatsData struct {
|
||||
Day string `json:"day"`
|
||||
SuccNum int64 `json:"succ_num"`
|
||||
FailedNum int64 `json:"failed_num"`
|
||||
TotalNum int64 `json:"total_num"`
|
||||
}
|
||||
|
||||
// StatusStatsData 状态统计数据
|
||||
type StatusStatsData struct {
|
||||
Status string `json:"status"`
|
||||
Num int64 `json:"num"`
|
||||
}
|
||||
|
||||
// TaskTypeStatsData 任务类型统计数据
|
||||
type TaskTypeStatsData struct {
|
||||
TaskType string `json:"task_type"`
|
||||
Num int64 `json:"num"`
|
||||
}
|
||||
|
||||
// GetSendStatsData 获取发送统计数据
|
||||
func GetSendStatsData(days int) (SendStatsData, error) {
|
||||
var result SendStatsData
|
||||
statsTable := GetSchema(SendStats{})
|
||||
currDay := util.GetNowTimeStr()[:10]
|
||||
|
||||
// 今日统计
|
||||
todayQuery := db.Table(statsTable).
|
||||
Select(`
|
||||
SUM(CASE WHEN status = 'success' THEN num ELSE 0 END) AS today_succ_num,
|
||||
SUM(CASE WHEN status = 'failed' THEN num ELSE 0 END) AS today_failed_num,
|
||||
SUM(num) AS today_total_num
|
||||
`).
|
||||
Where("day = ?", currDay)
|
||||
|
||||
todayQuery.Scan(&result)
|
||||
|
||||
// 总计统计
|
||||
totalQuery := db.Table(statsTable).
|
||||
Select(`
|
||||
SUM(CASE WHEN status = 'success' THEN num ELSE 0 END) AS total_succ_num,
|
||||
SUM(CASE WHEN status = 'failed' THEN num ELSE 0 END) AS total_failed_num,
|
||||
SUM(num) AS total_num
|
||||
`)
|
||||
|
||||
totalQuery.Scan(&result)
|
||||
|
||||
// 最近N天每日统计
|
||||
if days > 0 {
|
||||
now := util.GetNowTime()
|
||||
past := now.AddDate(0, 0, -days)
|
||||
pastDate := past.Format("2006-01-02")
|
||||
|
||||
var dailyStats []DailyStatsData
|
||||
dailyQuery := db.Table(statsTable).
|
||||
Select(`
|
||||
day,
|
||||
SUM(CASE WHEN status = 'success' THEN num ELSE 0 END) AS succ_num,
|
||||
SUM(CASE WHEN status = 'failed' THEN num ELSE 0 END) AS failed_num,
|
||||
SUM(num) AS total_num
|
||||
`).
|
||||
Where("day >= ?", pastDate).
|
||||
Group("day").
|
||||
Order("day DESC")
|
||||
|
||||
dailyQuery.Scan(&dailyStats)
|
||||
result.DailyStats = dailyStats
|
||||
}
|
||||
|
||||
// 状态分布统计
|
||||
var statusStats []StatusStatsData
|
||||
statusQuery := db.Table(statsTable).
|
||||
Select("status, SUM(num) AS num").
|
||||
Group("status").
|
||||
Order("num DESC")
|
||||
|
||||
statusQuery.Scan(&statusStats)
|
||||
result.StatusStats = statusStats
|
||||
|
||||
// 任务类型分布统计
|
||||
var taskTypeStats []TaskTypeStatsData
|
||||
taskTypeQuery := db.Table(statsTable).
|
||||
Select("task_type, SUM(num) AS num").
|
||||
Group("task_type").
|
||||
Order("num DESC")
|
||||
|
||||
taskTypeQuery.Scan(&taskTypeStats)
|
||||
result.TaskTypeStats = taskTypeStats
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetSendStatsByTask 获取指定任务的统计数据
|
||||
func GetSendStatsByTask(taskID uint, days int) (SendStatsData, error) {
|
||||
var result SendStatsData
|
||||
statsTable := GetSchema(SendStats{})
|
||||
currDay := util.GetNowTimeStr()[:10]
|
||||
|
||||
// 今日统计
|
||||
todayQuery := db.Table(statsTable).
|
||||
Select(`
|
||||
SUM(CASE WHEN status = 'success' THEN num ELSE 0 END) AS today_succ_num,
|
||||
SUM(CASE WHEN status = 'failed' THEN num ELSE 0 END) AS today_failed_num,
|
||||
SUM(num) AS today_total_num
|
||||
`).
|
||||
Where("day = ? AND task_id = ?", currDay, taskID)
|
||||
|
||||
todayQuery.Scan(&result)
|
||||
|
||||
// 总计统计
|
||||
totalQuery := db.Table(statsTable).
|
||||
Select(`
|
||||
SUM(CASE WHEN status = 'success' THEN num ELSE 0 END) AS total_succ_num,
|
||||
SUM(CASE WHEN status = 'failed' THEN num ELSE 0 END) AS total_failed_num,
|
||||
SUM(num) AS total_num
|
||||
`).
|
||||
Where("task_id = ?", taskID)
|
||||
|
||||
totalQuery.Scan(&result)
|
||||
|
||||
// 最近N天每日统计
|
||||
if days > 0 {
|
||||
now := util.GetNowTime()
|
||||
past := now.AddDate(0, 0, -days)
|
||||
pastDate := past.Format("2006-01-02")
|
||||
|
||||
var dailyStats []DailyStatsData
|
||||
dailyQuery := db.Table(statsTable).
|
||||
Select(`
|
||||
day,
|
||||
SUM(CASE WHEN status = 'success' THEN num ELSE 0 END) AS succ_num,
|
||||
SUM(CASE WHEN status = 'failed' THEN num ELSE 0 END) AS failed_num,
|
||||
SUM(num) AS total_num
|
||||
`).
|
||||
Where("day >= ? AND task_id = ?", pastDate, taskID).
|
||||
Group("day").
|
||||
Order("day DESC")
|
||||
|
||||
dailyQuery.Scan(&dailyStats)
|
||||
result.DailyStats = dailyStats
|
||||
}
|
||||
|
||||
// 状态分布统计
|
||||
var statusStats []StatusStatsData
|
||||
statusQuery := db.Table(statsTable).
|
||||
Select("status, SUM(num) AS num").
|
||||
Where("task_id = ?", taskID).
|
||||
Group("status").
|
||||
Order("num DESC")
|
||||
|
||||
statusQuery.Scan(&statusStats)
|
||||
result.StatusStats = statusStats
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// IncrementSendStats 增加发送统计(用于实时更新统计数据)
|
||||
func IncrementSendStats(taskID *uint, taskType string, day string, status string, num int64) error {
|
||||
statsTable := GetSchema(SendStats{})
|
||||
|
||||
// 使用 ON DUPLICATE KEY UPDATE 或 UPSERT 逻辑
|
||||
query := fmt.Sprintf(`
|
||||
INSERT INTO %s (task_id, task_type, day, status, num)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE num = num + ?
|
||||
`, statsTable)
|
||||
|
||||
return db.Exec(query, taskID, taskType, day, status, num, num).Error
|
||||
}
|
||||
+49
-28
@@ -55,7 +55,7 @@ func GetSendLogs(pageNum int, pageSize int, name string, taskId string, maps map
|
||||
}
|
||||
|
||||
query = query.Where(maps)
|
||||
|
||||
|
||||
// 按名称搜索(搜索日志表的 name 字段)
|
||||
if name != "" {
|
||||
query = query.Where(fmt.Sprintf("%s.name like ?", logt), fmt.Sprintf("%%%s%%", name))
|
||||
@@ -68,7 +68,7 @@ func GetSendLogs(pageNum int, pageSize int, name string, taskId string, maps map
|
||||
query = query.Offset(pageNum).Limit(pageSize)
|
||||
}
|
||||
query.Scan(&logs)
|
||||
|
||||
|
||||
//v1 接口的历史日志数据兼容处理
|
||||
// 应用层处理:为历史数据(type=task 且 name 为空)补充任务名称
|
||||
fillTaskNamesForLogs(&logs)
|
||||
@@ -130,7 +130,7 @@ func fillTaskNamesForLogs(logs *[]LogsResult) {
|
||||
func GetSendLogsTotal(name string, taskId string, maps map[string]interface{}) (int64, error) {
|
||||
var total int64
|
||||
logt := GetSchema(SendTasksLogs{})
|
||||
|
||||
|
||||
// 简化查询,只查询日志表
|
||||
query := db.Table(logt)
|
||||
|
||||
@@ -141,7 +141,7 @@ func GetSendLogsTotal(name string, taskId string, maps map[string]interface{}) (
|
||||
}
|
||||
|
||||
query = query.Where(maps)
|
||||
|
||||
|
||||
// 按名称搜索(搜索日志表的 name 字段)
|
||||
if name != "" {
|
||||
query = query.Where(fmt.Sprintf("%s.name like ?", logt), fmt.Sprintf("%%%s%%", name))
|
||||
@@ -156,7 +156,7 @@ func GetSendLogsTotal(name string, taskId string, maps map[string]interface{}) (
|
||||
// GetSendLogsTotal 获取所有日志总数
|
||||
func DeleteOutDateLogs(keepNum int) (int, error) {
|
||||
var affectedRows int
|
||||
|
||||
|
||||
// 优化方案:使用GORM的Offset和Limit找到临界ID,兼容多种数据库
|
||||
// 1. 获取第 keepNum 条记录的ID作为临界值
|
||||
var threshold SendTasksLogs
|
||||
@@ -166,18 +166,18 @@ func DeleteOutDateLogs(keepNum int) (int, error) {
|
||||
Offset(keepNum - 1).
|
||||
Limit(1).
|
||||
First(&threshold)
|
||||
|
||||
|
||||
// 如果记录总数不足keepNum条,则不需要删除
|
||||
if result.Error != nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
|
||||
// 2. 删除ID小于临界值的记录
|
||||
deleteResult := db.Where("id < ?", threshold.ID).Delete(&SendTasksLogs{})
|
||||
if deleteResult.Error != nil {
|
||||
return affectedRows, deleteResult.Error
|
||||
}
|
||||
|
||||
|
||||
affectedRows = int(deleteResult.RowsAffected)
|
||||
return affectedRows, nil
|
||||
}
|
||||
@@ -317,27 +317,27 @@ func GetBasicStatisticData() (BasicStatisticData, error) {
|
||||
return statistic, nil
|
||||
}
|
||||
|
||||
// GetTrendStatisticData 获取趋势统计数据
|
||||
// GetTrendStatisticData 获取趋势统计数据(使用 send_stats 表)
|
||||
func GetTrendStatisticData() (TrendStatisticData, error) {
|
||||
var statistic TrendStatisticData
|
||||
var latestData []LatestSendData
|
||||
logt := GetSchema(SendTasksLogs{})
|
||||
statsTable := GetSchema(SendStats{})
|
||||
|
||||
// 最近30天数据
|
||||
days := 30
|
||||
now := util.GetNowTime()
|
||||
past := now.AddDate(0, 0, -days)
|
||||
pastDate := past.Format("2006-01-02")
|
||||
next := now.AddDate(0, 0, 1)
|
||||
nextDate := next.Format("2006-01-02")
|
||||
|
||||
queryData := db.
|
||||
Table(logt).
|
||||
Table(statsTable).
|
||||
Select(`
|
||||
CAST(DATE(created_on) AS CHAR) AS day,
|
||||
SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS day_succ_num,
|
||||
SUM(CASE WHEN status != 1 or status is null THEN 1 ELSE 0 END) AS day_failed_num,
|
||||
COUNT(*) AS num`).
|
||||
Where(fmt.Sprintf(" created_on >= '%s' and created_on <= '%s' ", pastDate, nextDate)).
|
||||
day,
|
||||
SUM(CASE WHEN status = 'success' THEN num ELSE 0 END) AS day_succ_num,
|
||||
SUM(CASE WHEN status = 'failed' THEN num ELSE 0 END) AS day_failed_num,
|
||||
SUM(num) AS num
|
||||
`).
|
||||
Where("day >= ?", pastDate).
|
||||
Group("day").
|
||||
Order("day")
|
||||
|
||||
@@ -346,21 +346,42 @@ func GetTrendStatisticData() (TrendStatisticData, error) {
|
||||
return statistic, nil
|
||||
}
|
||||
|
||||
// GetChannelStatisticData 获取渠道统计数据(包含任务实例和模板实例)
|
||||
// GetChannelStatisticData 获取渠道统计数据(使用 send_stats 表的任务类型统计)
|
||||
func GetChannelStatisticData() (ChannelStatisticData, error) {
|
||||
var statistic ChannelStatisticData
|
||||
var wayCateData []WayCateData
|
||||
inst := GetSchema(SendTasksIns{})
|
||||
wayst := GetSchema(SendWays{})
|
||||
statsTable := GetSchema(SendStats{})
|
||||
|
||||
// 任务类型映射
|
||||
taskTypeMap := map[string]string{
|
||||
"task": "发信任务",
|
||||
"template": "模板任务",
|
||||
}
|
||||
|
||||
// 统计任务类型分布
|
||||
var taskTypeStats []struct {
|
||||
TaskType string
|
||||
Num int64
|
||||
}
|
||||
|
||||
// 统计所有实例的渠道分布(包含任务实例和模板实例)
|
||||
// 不区分 task_id 和 template_id,统计所有关联到渠道的实例
|
||||
db.
|
||||
Table(inst).
|
||||
Select(fmt.Sprintf("%s.name as way_name, count(%s.id) as count_num", wayst, inst)).
|
||||
Joins(fmt.Sprintf("JOIN %s ON %s.way_id = %s.id", wayst, inst, wayst)).
|
||||
Group(fmt.Sprintf("%s.id", wayst)).
|
||||
Scan(&wayCateData)
|
||||
Table(statsTable).
|
||||
Select("task_type, SUM(num) AS num").
|
||||
Group("task_type").
|
||||
Order("num DESC").
|
||||
Scan(&taskTypeStats)
|
||||
|
||||
// 转换为 WayCateData 格式(复用前端已有的数据结构)
|
||||
for _, stat := range taskTypeStats {
|
||||
wayName := taskTypeMap[stat.TaskType]
|
||||
if wayName == "" {
|
||||
wayName = stat.TaskType
|
||||
}
|
||||
wayCateData = append(wayCateData, WayCateData{
|
||||
WayName: wayName,
|
||||
CountNum: int(stat.Num),
|
||||
})
|
||||
}
|
||||
|
||||
statistic.WayCateData = wayCateData
|
||||
return statistic, nil
|
||||
|
||||
Reference in New Issue
Block a user