init push
This commit is contained in:
379
backend/repo/pg/stat_hour.go
Normal file
379
backend/repo/pg/stat_hour.go
Normal file
@@ -0,0 +1,379 @@
|
||||
package pg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo"
|
||||
|
||||
v1 "github.com/chaitin/panda-wiki/api/stat/v1"
|
||||
"github.com/chaitin/panda-wiki/domain"
|
||||
"github.com/chaitin/panda-wiki/utils"
|
||||
)
|
||||
|
||||
func (r *StatRepository) GetConversationCountOneHour(ctx context.Context, kbID string) (int64, error) {
|
||||
var conversationCount int64
|
||||
if err := r.db.WithContext(ctx).
|
||||
Model(&domain.Conversation{}).
|
||||
Where("kb_id = ?", kbID).
|
||||
Where("created_at >= ? AND created_at < ?", utils.GetTimeHourOffset(-1), utils.GetTimeHourOffset(0)).
|
||||
Count(&conversationCount).Error; err != nil {
|
||||
return conversationCount, err
|
||||
}
|
||||
return conversationCount, nil
|
||||
}
|
||||
|
||||
func (r *StatRepository) GetStatPageOneHour(ctx context.Context, kbID string) (*domain.StatPageHour, error) {
|
||||
var statPageHour domain.StatPageHour
|
||||
err := r.db.WithContext(ctx).Table("stat_pages").
|
||||
Select(`
|
||||
COUNT(DISTINCT ip) as ip_count,
|
||||
COUNT(DISTINCT session_id) as session_count,
|
||||
COUNT(*) as page_visit_count
|
||||
`).
|
||||
Where("created_at >= ? AND created_at < ?", utils.GetTimeHourOffset(-1), utils.GetTimeHourOffset(0)).
|
||||
Where("kb_id = ?", kbID).
|
||||
Find(&statPageHour).Error
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &statPageHour, nil
|
||||
}
|
||||
|
||||
func (r *StatRepository) GetGeCountOneHour(ctx context.Context, kbID string) (map[string]int64, error) {
|
||||
key := fmt.Sprintf("geo:%s:%s", kbID, time.Now().Add(-time.Duration(1)*time.Hour).Format("2006-01-02-15"))
|
||||
values, err := r.cache.HGetAll(ctx, key).Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
geoCount := make(map[string]int64)
|
||||
for field, value := range values {
|
||||
valueInt, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse geo count failed: %w", err)
|
||||
}
|
||||
geoCount[field] += valueInt
|
||||
}
|
||||
|
||||
return geoCount, nil
|
||||
}
|
||||
|
||||
func (r *StatRepository) GetConversationDistributionOneHour(ctx context.Context, kbID string) (map[string]int64, error) {
|
||||
var cds []domain.ConversationDistribution
|
||||
if err := r.db.WithContext(ctx).
|
||||
Model(&domain.Conversation{}).
|
||||
Select("apps.type as app_type", "COUNT(*) as count").
|
||||
Joins("left join apps on apps.id=conversations.app_id").
|
||||
Where("conversations.kb_id = ?", kbID).
|
||||
Where("conversations.created_at >= ? AND conversations.created_at < ?", utils.GetTimeHourOffset(-1), utils.GetTimeHourOffset(0)).
|
||||
Group("apps.type").
|
||||
Find(&cds).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(cds) == 0 {
|
||||
return make(map[string]int64), nil
|
||||
}
|
||||
|
||||
dcCount := lo.SliceToMap(cds, func(cd domain.ConversationDistribution) (string, int64) {
|
||||
return strconv.Itoa(int(cd.AppType)), cd.Count
|
||||
})
|
||||
|
||||
return dcCount, nil
|
||||
}
|
||||
|
||||
func (r *StatRepository) GetHotRefererHostOneHour(ctx context.Context, kbID string) (map[string]int64, error) {
|
||||
var hotRefererHosts []*domain.HotRefererHost
|
||||
if err := r.db.WithContext(ctx).Model(&domain.StatPage{}).
|
||||
Where("kb_id = ?", kbID).
|
||||
Where("created_at >= ? AND created_at < ?", utils.GetTimeHourOffset(-1), utils.GetTimeHourOffset(0)).
|
||||
Group("referer_host").
|
||||
Select("referer_host, COUNT(*) as count").
|
||||
Order("count DESC").
|
||||
Limit(10).
|
||||
Find(&hotRefererHosts).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(hotRefererHosts) == 0 {
|
||||
return make(map[string]int64), nil
|
||||
}
|
||||
|
||||
refererHostCount := lo.SliceToMap(hotRefererHosts, func(item *domain.HotRefererHost) (string, int64) {
|
||||
return item.RefererHost, item.Count
|
||||
})
|
||||
|
||||
return refererHostCount, nil
|
||||
}
|
||||
|
||||
func (r *StatRepository) GetHotRefererHostsByHour(ctx context.Context, kbID string, startHour int64) (map[string]int64, error) {
|
||||
// 查询实时数据
|
||||
var hotRefererHosts []*domain.HotRefererHost
|
||||
if err := r.db.WithContext(ctx).Model(&domain.StatPage{}).
|
||||
Where("kb_id = ?", kbID).
|
||||
Where("referer_host != '' ").
|
||||
Where("created_at > ?", utils.GetTimeHourOffset(-24)).
|
||||
Group("referer_host").
|
||||
Select("referer_host, COUNT(*) as count").
|
||||
Order("count DESC").
|
||||
Limit(10).
|
||||
Find(&hotRefererHosts).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 查询小时统计表中的聚合数据
|
||||
statPageHours := make([]domain.StatPageHour, 0)
|
||||
if err := r.db.WithContext(ctx).Model(&domain.StatPageHour{}).
|
||||
Select("hot_referer_host").
|
||||
Where("kb_id = ?", kbID).
|
||||
Where("hour >= ? and hour < ?", utils.GetTimeHourOffset(-startHour), utils.GetTimeHourOffset(-24)).
|
||||
Find(&statPageHours).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 聚合小时统计数据
|
||||
refererHostCountMap := make(map[string]int64)
|
||||
for i := range statPageHours {
|
||||
for k, v := range statPageHours[i].HotRefererHost {
|
||||
refererHostCountMap[k] += v
|
||||
}
|
||||
}
|
||||
|
||||
// 合并实时数据和聚合数据
|
||||
finalRefererHostCount := make(map[string]int64)
|
||||
for _, item := range hotRefererHosts {
|
||||
finalRefererHostCount[item.RefererHost] = item.Count
|
||||
}
|
||||
|
||||
for host, count := range refererHostCountMap {
|
||||
if host != "" {
|
||||
finalRefererHostCount[host] += count
|
||||
}
|
||||
}
|
||||
|
||||
return finalRefererHostCount, nil
|
||||
}
|
||||
|
||||
func (r *StatRepository) CreateStatPageHour(ctx context.Context, statPageHour *domain.StatPageHour) error {
|
||||
return r.db.WithContext(ctx).Create(statPageHour).Error
|
||||
}
|
||||
|
||||
// CheckStatPageHourExists 检查指定时间和知识库的小时统计数据是否已存在
|
||||
func (r *StatRepository) CheckStatPageHourExists(ctx context.Context, kbID string, hour time.Time) (bool, error) {
|
||||
var count int64
|
||||
err := r.db.WithContext(ctx).Model(&domain.StatPageHour{}).
|
||||
Where("kb_id = ? AND hour = ?", kbID, hour).
|
||||
Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// CleanupOldHourlyStats 清理90天前的小时统计数据
|
||||
func (r *StatRepository) CleanupOldHourlyStats(ctx context.Context) error {
|
||||
return r.db.WithContext(ctx).Model(&domain.StatPageHour{}).
|
||||
Where("hour < NOW() - INTERVAL '90 days'").
|
||||
Delete(&domain.StatPageHour{}).Error
|
||||
}
|
||||
|
||||
func (r *StatRepository) GetHotPagesOneHour(ctx context.Context, kbID string) (map[string]int64, error) {
|
||||
var hotPages []*domain.HotPage
|
||||
if err := r.db.WithContext(ctx).Model(&domain.StatPage{}).
|
||||
Where("kb_id = ?", kbID).
|
||||
Where("node_id != '' ").
|
||||
Where("scene = ?", domain.StatPageSceneNodeDetail).
|
||||
Where("created_at >= ? AND created_at < ?", utils.GetTimeHourOffset(-1), utils.GetTimeHourOffset(0)).
|
||||
Group("node_id").
|
||||
Select("node_id, COUNT(*) as count").
|
||||
Order("count DESC").
|
||||
Find(&hotPages).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(hotPages) == 0 {
|
||||
return make(map[string]int64), nil
|
||||
}
|
||||
|
||||
refererHostCount := lo.SliceToMap(hotPages, func(item *domain.HotPage) (string, int64) {
|
||||
return item.NodeID, item.Count
|
||||
})
|
||||
|
||||
return refererHostCount, nil
|
||||
}
|
||||
|
||||
func (r *StatRepository) GetHotPagesByHour(ctx context.Context, kbID string, startHour int64) (map[string]int64, error) {
|
||||
// 查询小时统计表中的聚合数据
|
||||
counts := make(map[string]int64)
|
||||
hotPageMaps := make([]domain.MapStrInt64, 0)
|
||||
if err := r.db.WithContext(ctx).Model(&domain.StatPageHour{}).
|
||||
Where("kb_id = ?", kbID).
|
||||
Where("hot_page != '{}'").
|
||||
Where("hour >= ? and hour < ?", utils.GetTimeHourOffset(-startHour), utils.GetTimeHourOffset(-24)).
|
||||
Pluck("hot_page", &hotPageMaps).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range hotPageMaps {
|
||||
for k, v := range hotPageMaps[i] {
|
||||
counts[k] += v
|
||||
}
|
||||
}
|
||||
|
||||
return counts, nil
|
||||
}
|
||||
|
||||
func (r *StatRepository) GetHotBrowsersOneHour(ctx context.Context, kbID string) (map[string]int64, error) {
|
||||
var browserCount []domain.BrowserCount
|
||||
|
||||
query := r.db.WithContext(ctx).Model(&domain.StatPage{}).
|
||||
Where("kb_id = ?", kbID).
|
||||
Where("created_at >= ? AND created_at < ?", utils.GetTimeHourOffset(-1), utils.GetTimeHourOffset(0)).
|
||||
Group("browser_name").
|
||||
Select("browser_name as name, COUNT(*) as count")
|
||||
if err := query.Order("count DESC").Limit(10).Find(&browserCount).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(browserCount) == 0 {
|
||||
return make(map[string]int64), nil
|
||||
}
|
||||
|
||||
refererHostCount := lo.SliceToMap(browserCount, func(item domain.BrowserCount) (string, int64) {
|
||||
return item.Name, item.Count
|
||||
})
|
||||
|
||||
return refererHostCount, nil
|
||||
}
|
||||
|
||||
func (r *StatRepository) GetHotOSOneHour(ctx context.Context, kbID string) (map[string]int64, error) {
|
||||
var osCount []domain.BrowserCount
|
||||
|
||||
query := r.db.WithContext(ctx).Model(&domain.StatPage{}).
|
||||
Where("kb_id = ?", kbID).
|
||||
Where("created_at >= ? AND created_at < ?", utils.GetTimeHourOffset(-1), utils.GetTimeHourOffset(0)).
|
||||
Group("browser_os").
|
||||
Select("browser_os as name, COUNT(*) as count")
|
||||
if err := query.Order("count DESC").Limit(10).Find(&osCount).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(osCount) == 0 {
|
||||
return make(map[string]int64), nil
|
||||
}
|
||||
|
||||
refererOSCount := lo.SliceToMap(osCount, func(item domain.BrowserCount) (string, int64) {
|
||||
return item.Name, item.Count
|
||||
})
|
||||
|
||||
return refererOSCount, nil
|
||||
}
|
||||
|
||||
func (r *StatRepository) GetStatPageCountByHour(ctx context.Context, kbID string, startHour int64) (*v1.StatCountResp, error) {
|
||||
var count v1.StatCountResp
|
||||
if err := r.db.WithContext(ctx).Model(&domain.StatPageHour{}).
|
||||
Select("SUM(ip_count) as ip_count, SUM(session_count) as session_count, SUM(page_visit_count) as page_visit_count, SUM(conversation_count) as conversation_count").
|
||||
Where("kb_id = ?", kbID).
|
||||
Where("hour >= ? and hour < ?", utils.GetTimeHourOffset(-startHour), utils.GetTimeHourOffset(-24)).
|
||||
Scan(&count).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &count, nil
|
||||
}
|
||||
|
||||
func (r *StatRepository) GetHotBrowsersByHour(ctx context.Context, kbID string, startHour int64) (*domain.HotBrowser, error) {
|
||||
|
||||
var browserCount []domain.BrowserCount
|
||||
query := r.db.WithContext(ctx).Model(&domain.StatPage{}).
|
||||
Where("kb_id = ?", kbID).
|
||||
Where("created_at > ?", utils.GetTimeHourOffset(-24)).
|
||||
Where("browser_name != '' ").
|
||||
Group("browser_name").
|
||||
Select("browser_name as name, COUNT(*) as count")
|
||||
if err := query.Order("count DESC").Find(&browserCount).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var osCount []domain.BrowserCount
|
||||
query = r.db.WithContext(ctx).Model(&domain.StatPage{}).
|
||||
Where("kb_id = ?", kbID).
|
||||
Where("created_at > ?", utils.GetTimeHourOffset(-24)).
|
||||
Where("browser_os != '' ").
|
||||
Group("browser_os").
|
||||
Select("browser_os as name, COUNT(*) as count")
|
||||
if err := query.Order("count DESC").Find(&osCount).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
statPageHours := make([]domain.StatPageHour, 0)
|
||||
if err := r.db.WithContext(ctx).Model(&domain.StatPageHour{}).
|
||||
Select("hot_os, hot_browser").
|
||||
Where("kb_id = ?", kbID).
|
||||
Where("hour >= ? and hour < ?", utils.GetTimeHourOffset(-startHour), utils.GetTimeHourOffset(-24)).
|
||||
Find(&statPageHours).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hourBrowserCountMap := make(domain.MapStrInt64)
|
||||
hourOSCountMap := make(domain.MapStrInt64)
|
||||
|
||||
for i := range statPageHours {
|
||||
for k, v := range statPageHours[i].HotOS {
|
||||
if k != "" {
|
||||
hourOSCountMap[k] += v
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range statPageHours[i].HotBrowser {
|
||||
if k != "" {
|
||||
hourBrowserCountMap[k] += v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i := range browserCount {
|
||||
hourBrowserCountMap[browserCount[i].Name] += browserCount[i].Count
|
||||
}
|
||||
|
||||
for i := range osCount {
|
||||
hourOSCountMap[osCount[i].Name] += osCount[i].Count
|
||||
}
|
||||
|
||||
browserCount = lo.MapToSlice(hourBrowserCountMap, func(k string, v int64) domain.BrowserCount {
|
||||
return domain.BrowserCount{
|
||||
Name: k,
|
||||
Count: v,
|
||||
}
|
||||
})
|
||||
|
||||
osCount = lo.MapToSlice(hourOSCountMap, func(k string, v int64) domain.BrowserCount {
|
||||
return domain.BrowserCount{
|
||||
Name: k,
|
||||
Count: v,
|
||||
}
|
||||
})
|
||||
|
||||
// Sort browserCount by count in descending order and take top 10
|
||||
sort.Slice(browserCount, func(i, j int) bool {
|
||||
return browserCount[i].Count > browserCount[j].Count
|
||||
})
|
||||
if len(browserCount) > 10 {
|
||||
browserCount = browserCount[:10]
|
||||
}
|
||||
|
||||
// Sort osCount by count in descending order and take top 10
|
||||
sort.Slice(osCount, func(i, j int) bool {
|
||||
return osCount[i].Count > osCount[j].Count
|
||||
})
|
||||
if len(osCount) > 10 {
|
||||
osCount = osCount[:10]
|
||||
}
|
||||
|
||||
return &domain.HotBrowser{
|
||||
Browser: browserCount,
|
||||
OS: osCount,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user