2025-07-10 21:14:17 +08:00
|
|
|
package repo
|
|
|
|
|
|
|
|
|
|
import (
|
2025-07-21 23:38:28 +08:00
|
|
|
"fmt"
|
2025-07-10 21:14:17 +08:00
|
|
|
|
2025-07-18 09:42:07 +08:00
|
|
|
"github.com/ctwj/urldb/db/entity"
|
2025-08-11 01:34:07 +08:00
|
|
|
"github.com/ctwj/urldb/utils"
|
2025-07-10 21:14:17 +08:00
|
|
|
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// SearchStatRepository 搜索统计Repository接口
|
|
|
|
|
type SearchStatRepository interface {
|
|
|
|
|
BaseRepository[entity.SearchStat]
|
|
|
|
|
RecordSearch(keyword, ip, userAgent string) error
|
|
|
|
|
GetDailyStats(days int) ([]entity.DailySearchStat, error)
|
|
|
|
|
GetHotKeywords(days int, limit int) ([]entity.KeywordStat, error)
|
|
|
|
|
GetSearchTrend(days int) ([]entity.DailySearchStat, error)
|
|
|
|
|
GetKeywordTrend(keyword string, days int) ([]entity.DailySearchStat, error)
|
2025-07-21 23:38:28 +08:00
|
|
|
GetSummary() (map[string]int64, error)
|
2025-08-13 15:22:01 +08:00
|
|
|
FindWithPaginationOrdered(page, limit int) ([]entity.SearchStat, int64, error)
|
2025-07-10 21:14:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SearchStatRepositoryImpl 搜索统计Repository实现
|
|
|
|
|
type SearchStatRepositoryImpl struct {
|
|
|
|
|
BaseRepositoryImpl[entity.SearchStat]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewSearchStatRepository 创建搜索统计Repository
|
|
|
|
|
func NewSearchStatRepository(db *gorm.DB) SearchStatRepository {
|
|
|
|
|
return &SearchStatRepositoryImpl{
|
|
|
|
|
BaseRepositoryImpl: BaseRepositoryImpl[entity.SearchStat]{db: db},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-21 23:38:28 +08:00
|
|
|
// RecordSearch 记录搜索(每次都插入新记录)
|
2025-07-10 21:14:17 +08:00
|
|
|
func (r *SearchStatRepositoryImpl) RecordSearch(keyword, ip, userAgent string) error {
|
2025-07-21 23:38:28 +08:00
|
|
|
stat := entity.SearchStat{
|
|
|
|
|
Keyword: keyword,
|
|
|
|
|
Count: 1,
|
2025-08-11 01:34:07 +08:00
|
|
|
Date: utils.GetCurrentTime(), // 可保留 date 字段,实际用 created_at 统计
|
2025-07-21 23:38:28 +08:00
|
|
|
IP: ip,
|
|
|
|
|
UserAgent: userAgent,
|
2025-07-10 21:14:17 +08:00
|
|
|
}
|
2025-07-21 23:38:28 +08:00
|
|
|
return r.db.Create(&stat).Error
|
2025-07-10 21:14:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetDailyStats 获取每日统计
|
|
|
|
|
func (r *SearchStatRepositoryImpl) GetDailyStats(days int) ([]entity.DailySearchStat, error) {
|
|
|
|
|
var stats []entity.DailySearchStat
|
|
|
|
|
|
2025-07-21 23:38:28 +08:00
|
|
|
query := fmt.Sprintf(`
|
2025-07-10 21:14:17 +08:00
|
|
|
SELECT
|
|
|
|
|
date,
|
|
|
|
|
SUM(count) as total_searches,
|
|
|
|
|
COUNT(DISTINCT keyword) as unique_keywords
|
|
|
|
|
FROM search_stats
|
2025-07-21 23:38:28 +08:00
|
|
|
WHERE date >= CURRENT_DATE - INTERVAL '%d days'
|
2025-07-10 21:14:17 +08:00
|
|
|
GROUP BY date
|
|
|
|
|
ORDER BY date DESC
|
2025-07-21 23:38:28 +08:00
|
|
|
`, days)
|
2025-07-10 21:14:17 +08:00
|
|
|
|
2025-07-21 23:38:28 +08:00
|
|
|
err := r.db.Raw(query).Scan(&stats).Error
|
2025-07-10 21:14:17 +08:00
|
|
|
return stats, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetHotKeywords 获取热门关键词
|
|
|
|
|
func (r *SearchStatRepositoryImpl) GetHotKeywords(days int, limit int) ([]entity.KeywordStat, error) {
|
|
|
|
|
var keywords []entity.KeywordStat
|
|
|
|
|
|
2025-07-21 23:38:28 +08:00
|
|
|
query := fmt.Sprintf(`
|
2025-07-10 21:14:17 +08:00
|
|
|
SELECT
|
|
|
|
|
keyword,
|
|
|
|
|
SUM(count) as count,
|
|
|
|
|
RANK() OVER (ORDER BY SUM(count) DESC) as rank
|
|
|
|
|
FROM search_stats
|
2025-07-21 23:38:28 +08:00
|
|
|
WHERE date >= CURRENT_DATE - INTERVAL '%d days'
|
2025-07-10 21:14:17 +08:00
|
|
|
GROUP BY keyword
|
|
|
|
|
ORDER BY count DESC
|
|
|
|
|
LIMIT ?
|
2025-07-21 23:38:28 +08:00
|
|
|
`, days)
|
2025-07-10 21:14:17 +08:00
|
|
|
|
2025-07-21 23:38:28 +08:00
|
|
|
err := r.db.Raw(query, limit).Scan(&keywords).Error
|
2025-07-10 21:14:17 +08:00
|
|
|
return keywords, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetSearchTrend 获取搜索趋势
|
|
|
|
|
func (r *SearchStatRepositoryImpl) GetSearchTrend(days int) ([]entity.DailySearchStat, error) {
|
|
|
|
|
var stats []entity.DailySearchStat
|
|
|
|
|
|
2025-07-21 23:38:28 +08:00
|
|
|
query := fmt.Sprintf(`
|
2025-07-10 21:14:17 +08:00
|
|
|
SELECT
|
|
|
|
|
date,
|
|
|
|
|
SUM(count) as total_searches,
|
|
|
|
|
COUNT(DISTINCT keyword) as unique_keywords
|
|
|
|
|
FROM search_stats
|
2025-07-21 23:38:28 +08:00
|
|
|
WHERE date >= CURRENT_DATE - INTERVAL '%d days'
|
2025-07-10 21:14:17 +08:00
|
|
|
GROUP BY date
|
|
|
|
|
ORDER BY date ASC
|
2025-07-21 23:38:28 +08:00
|
|
|
`, days)
|
2025-07-10 21:14:17 +08:00
|
|
|
|
2025-07-21 23:38:28 +08:00
|
|
|
err := r.db.Raw(query).Scan(&stats).Error
|
2025-07-10 21:14:17 +08:00
|
|
|
return stats, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetKeywordTrend 获取关键词趋势
|
|
|
|
|
func (r *SearchStatRepositoryImpl) GetKeywordTrend(keyword string, days int) ([]entity.DailySearchStat, error) {
|
|
|
|
|
var stats []entity.DailySearchStat
|
|
|
|
|
|
2025-07-21 23:38:28 +08:00
|
|
|
query := fmt.Sprintf(`
|
2025-07-10 21:14:17 +08:00
|
|
|
SELECT
|
|
|
|
|
date,
|
|
|
|
|
SUM(count) as total_searches,
|
|
|
|
|
COUNT(DISTINCT keyword) as unique_keywords
|
|
|
|
|
FROM search_stats
|
2025-07-21 23:38:28 +08:00
|
|
|
WHERE keyword = ? AND date >= CURRENT_DATE - INTERVAL '%d days'
|
2025-07-10 21:14:17 +08:00
|
|
|
GROUP BY date
|
|
|
|
|
ORDER BY date ASC
|
2025-07-21 23:38:28 +08:00
|
|
|
`, days)
|
2025-07-10 21:14:17 +08:00
|
|
|
|
2025-07-21 23:38:28 +08:00
|
|
|
err := r.db.Raw(query, keyword).Scan(&stats).Error
|
2025-07-10 21:14:17 +08:00
|
|
|
return stats, err
|
|
|
|
|
}
|
2025-07-21 23:38:28 +08:00
|
|
|
|
|
|
|
|
// GetSummary 获取搜索统计汇总
|
|
|
|
|
func (r *SearchStatRepositoryImpl) GetSummary() (map[string]int64, error) {
|
|
|
|
|
var total, today, week, month, keywords int64
|
2025-08-11 01:34:07 +08:00
|
|
|
now := utils.GetCurrentTime()
|
|
|
|
|
todayStr := now.Format(utils.TimeFormatDate)
|
|
|
|
|
weekStart := now.AddDate(0, 0, -int(now.Weekday())+1).Format(utils.TimeFormatDate) // 周一
|
2025-07-21 23:38:28 +08:00
|
|
|
monthStart := now.Format("2006-01") + "-01"
|
|
|
|
|
|
|
|
|
|
// 总搜索次数
|
|
|
|
|
if err := r.db.Model(&entity.SearchStat{}).Count(&total).Error; err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
// 今日搜索次数
|
|
|
|
|
if err := r.db.Model(&entity.SearchStat{}).Where("DATE(created_at) = ?", todayStr).Count(&today).Error; err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
// 本周搜索次数
|
|
|
|
|
if err := r.db.Model(&entity.SearchStat{}).Where("created_at >= ?", weekStart).Count(&week).Error; err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
// 本月搜索次数
|
|
|
|
|
if err := r.db.Model(&entity.SearchStat{}).Where("created_at >= ?", monthStart).Count(&month).Error; err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
// 总关键词数
|
|
|
|
|
if err := r.db.Model(&entity.SearchStat{}).Distinct("keyword").Count(&keywords).Error; err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return map[string]int64{
|
|
|
|
|
"total": total,
|
|
|
|
|
"today": today,
|
|
|
|
|
"week": week,
|
|
|
|
|
"month": month,
|
|
|
|
|
"keywords": keywords,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
2025-08-13 15:22:01 +08:00
|
|
|
|
|
|
|
|
// FindWithPaginationOrdered 按时间倒序分页查找搜索记录
|
|
|
|
|
func (r *SearchStatRepositoryImpl) FindWithPaginationOrdered(page, limit int) ([]entity.SearchStat, int64, error) {
|
|
|
|
|
var stats []entity.SearchStat
|
|
|
|
|
var total int64
|
|
|
|
|
|
|
|
|
|
offset := (page - 1) * limit
|
|
|
|
|
|
|
|
|
|
// 获取总数
|
|
|
|
|
if err := r.db.Model(&entity.SearchStat{}).Count(&total).Error; err != nil {
|
|
|
|
|
return nil, 0, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取分页数据,按创建时间倒序排列(最新的在前面)
|
|
|
|
|
err := r.db.Order("created_at DESC").Offset(offset).Limit(limit).Find(&stats).Error
|
|
|
|
|
return stats, total, err
|
|
|
|
|
}
|