243 lines
6.5 KiB
Go
243 lines
6.5 KiB
Go
// Package dao 提供 admin 模块的数据库访问操作
|
||
package dao
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/echochat/backend/app/dto"
|
||
imModel "github.com/echochat/backend/app/im/model"
|
||
"github.com/echochat/backend/pkg/logs"
|
||
"go.uber.org/zap"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// MessageManageDAO 管理端消息数据访问对象
|
||
type MessageManageDAO struct {
|
||
db *gorm.DB
|
||
}
|
||
|
||
// NewMessageManageDAO 创建 MessageManageDAO 实例
|
||
func NewMessageManageDAO(db *gorm.DB) *MessageManageDAO {
|
||
return &MessageManageDAO{db: db}
|
||
}
|
||
|
||
// ListMessages 分页查询消息列表(支持多条件筛选)
|
||
func (d *MessageManageDAO) ListMessages(ctx context.Context, req *dto.AdminMessageListRequest) ([]imModel.Message, int64, error) {
|
||
funcName := "dao.message_manage_dao.ListMessages"
|
||
logs.Debug(ctx, funcName, "查询消息列表")
|
||
|
||
query := d.db.WithContext(ctx).Model(&imModel.Message{})
|
||
|
||
if req.Keyword != "" {
|
||
query = query.Where("content ILIKE ?", fmt.Sprintf("%%%s%%", req.Keyword))
|
||
}
|
||
if req.Type != nil {
|
||
query = query.Where("type = ?", *req.Type)
|
||
}
|
||
if req.SenderID != nil {
|
||
query = query.Where("sender_id = ?", *req.SenderID)
|
||
}
|
||
if req.ConversationID != nil {
|
||
query = query.Where("conversation_id = ?", *req.ConversationID)
|
||
}
|
||
if req.Status != nil {
|
||
query = query.Where("status = ?", *req.Status)
|
||
}
|
||
if req.StartTime != "" {
|
||
if t, err := time.Parse("2006-01-02", req.StartTime); err == nil {
|
||
query = query.Where("created_at >= ?", t)
|
||
}
|
||
}
|
||
if req.EndTime != "" {
|
||
if t, err := time.Parse("2006-01-02", req.EndTime); err == nil {
|
||
query = query.Where("created_at < ?", t.AddDate(0, 0, 1))
|
||
}
|
||
}
|
||
|
||
var total int64
|
||
if err := query.Count(&total).Error; err != nil {
|
||
logs.Error(ctx, funcName, "统计消息总数失败", zap.Error(err))
|
||
return nil, 0, err
|
||
}
|
||
|
||
page := req.Page
|
||
if page <= 0 {
|
||
page = 1
|
||
}
|
||
pageSize := req.PageSize
|
||
if pageSize <= 0 {
|
||
pageSize = 20
|
||
}
|
||
if pageSize > 100 {
|
||
pageSize = 100
|
||
}
|
||
|
||
var messages []imModel.Message
|
||
err := query.Order("id DESC").
|
||
Offset((page - 1) * pageSize).
|
||
Limit(pageSize).
|
||
Find(&messages).Error
|
||
if err != nil {
|
||
logs.Error(ctx, funcName, "查询消息列表失败", zap.Error(err))
|
||
return nil, 0, err
|
||
}
|
||
|
||
return messages, total, nil
|
||
}
|
||
|
||
// GetMessageByID 根据 ID 查询单条消息
|
||
func (d *MessageManageDAO) GetMessageByID(ctx context.Context, id int64) (*imModel.Message, error) {
|
||
var msg imModel.Message
|
||
err := d.db.WithContext(ctx).First(&msg, id).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &msg, nil
|
||
}
|
||
|
||
// UpdateMessageStatus 更新消息状态
|
||
func (d *MessageManageDAO) UpdateMessageStatus(ctx context.Context, id int64, status int) error {
|
||
return d.db.WithContext(ctx).
|
||
Model(&imModel.Message{}).
|
||
Where("id = ?", id).
|
||
Update("status", status).Error
|
||
}
|
||
|
||
// GetTotalCount 获取消息总数
|
||
func (d *MessageManageDAO) GetTotalCount(ctx context.Context) (int64, error) {
|
||
var count int64
|
||
err := d.db.WithContext(ctx).Model(&imModel.Message{}).Count(&count).Error
|
||
return count, err
|
||
}
|
||
|
||
// GetTodayCount 获取今日消息数
|
||
func (d *MessageManageDAO) GetTodayCount(ctx context.Context) (int64, error) {
|
||
var count int64
|
||
today := time.Now().Truncate(24 * time.Hour)
|
||
err := d.db.WithContext(ctx).Model(&imModel.Message{}).
|
||
Where("created_at >= ?", today).Count(&count).Error
|
||
return count, err
|
||
}
|
||
|
||
// GetTypeDistribution 获取消息类型分布
|
||
func (d *MessageManageDAO) GetTypeDistribution(ctx context.Context) ([]dto.TypeDistItem, error) {
|
||
type row struct {
|
||
Type int `gorm:"column:type"`
|
||
Count int64 `gorm:"column:count"`
|
||
}
|
||
var rows []row
|
||
err := d.db.WithContext(ctx).
|
||
Model(&imModel.Message{}).
|
||
Select("type, count(*) as count").
|
||
Group("type").
|
||
Order("count DESC").
|
||
Find(&rows).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
result := make([]dto.TypeDistItem, 0, len(rows))
|
||
for _, r := range rows {
|
||
result = append(result, dto.TypeDistItem{
|
||
Type: r.Type,
|
||
Count: r.Count,
|
||
})
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// GetDailyTrend 获取每日消息趋势
|
||
func (d *MessageManageDAO) GetDailyTrend(ctx context.Context, days int) ([]dto.DailyTrendItem, error) {
|
||
startDate := time.Now().AddDate(0, 0, -days).Truncate(24 * time.Hour)
|
||
type row struct {
|
||
Date string `gorm:"column:date"`
|
||
Count int64 `gorm:"column:count"`
|
||
}
|
||
var rows []row
|
||
err := d.db.WithContext(ctx).
|
||
Model(&imModel.Message{}).
|
||
Select("TO_CHAR(created_at, 'YYYY-MM-DD') as date, count(*) as count").
|
||
Where("created_at >= ?", startDate).
|
||
Group("date").
|
||
Order("date ASC").
|
||
Find(&rows).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
result := make([]dto.DailyTrendItem, 0, len(rows))
|
||
for _, r := range rows {
|
||
result = append(result, dto.DailyTrendItem{Date: r.Date, Count: r.Count})
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// GetActiveUsers 获取活跃用户排行(Top 10)
|
||
func (d *MessageManageDAO) GetActiveUsers(ctx context.Context, days int) ([]dto.ActiveUserItem, error) {
|
||
startDate := time.Now().AddDate(0, 0, -days).Truncate(24 * time.Hour)
|
||
type row struct {
|
||
UserID int64 `gorm:"column:sender_id"`
|
||
Nickname string `gorm:"column:nickname"`
|
||
Count int64 `gorm:"column:count"`
|
||
}
|
||
var rows []row
|
||
err := d.db.WithContext(ctx).
|
||
Raw(`SELECT m.sender_id, u.nickname, count(*) as count
|
||
FROM im_messages m
|
||
JOIN auth_users u ON u.id = m.sender_id
|
||
WHERE m.created_at >= ? AND m.sender_id > 0
|
||
GROUP BY m.sender_id, u.nickname
|
||
ORDER BY count DESC
|
||
LIMIT 10`, startDate).
|
||
Scan(&rows).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
result := make([]dto.ActiveUserItem, 0, len(rows))
|
||
for _, r := range rows {
|
||
result = append(result, dto.ActiveUserItem{
|
||
UserID: r.UserID,
|
||
Nickname: r.Nickname,
|
||
Count: r.Count,
|
||
})
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// GetActiveGroups 获取活跃群组排行(Top 10)
|
||
func (d *MessageManageDAO) GetActiveGroups(ctx context.Context, days int) ([]dto.ActiveGroupItem, error) {
|
||
startDate := time.Now().AddDate(0, 0, -days).Truncate(24 * time.Hour)
|
||
type row struct {
|
||
GroupID int64 `gorm:"column:id"`
|
||
Name string `gorm:"column:name"`
|
||
Count int64 `gorm:"column:count"`
|
||
}
|
||
var rows []row
|
||
err := d.db.WithContext(ctx).
|
||
Raw(`SELECT g.id, g.name, count(*) as count
|
||
FROM im_messages m
|
||
JOIN im_conversations c ON c.id = m.conversation_id
|
||
JOIN im_groups g ON g.conversation_id = c.id
|
||
WHERE m.created_at >= ? AND c.type = 2
|
||
GROUP BY g.id, g.name
|
||
ORDER BY count DESC
|
||
LIMIT 10`, startDate).
|
||
Scan(&rows).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
result := make([]dto.ActiveGroupItem, 0, len(rows))
|
||
for _, r := range rows {
|
||
result = append(result, dto.ActiveGroupItem{
|
||
GroupID: r.GroupID,
|
||
Name: r.Name,
|
||
Count: r.Count,
|
||
})
|
||
}
|
||
return result, nil
|
||
}
|