mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-28 05:46:36 +08:00
288b74d104
Result: {"status":"keep","total_issues":33,"golint_canonicalheader":0,"golint_errname":1,"golint_errorlint":12,"golint_forcetypeassert":3,"golint_gosec":0,"golint_intrange":0,"golint_modernize":3,"golint_nilnil":3,"golint_perfsprint":0,"golint_prealloc":3,"golint_recvcheck":7,"golint_usestdlibvars":0,"golint_wastedassign":0,"golint_total":32,"eslint_problems":1,"eslint_errors":0,"eslint_warnings":1,"tsc_errors":0,"measure_s":45}
162 lines
4.0 KiB
Go
162 lines
4.0 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package analytics
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sort"
|
|
"time"
|
|
|
|
db "github.com/Rain-kl/Wavelet/internal/infra/persistence"
|
|
analyticsmodel "github.com/Rain-kl/Wavelet/internal/model/analytics"
|
|
)
|
|
|
|
const hoursInDay = 24
|
|
|
|
// DailyTrend is a single day's access count.
|
|
type DailyTrend = analyticsmodel.DailyTrend
|
|
|
|
// BrowserShare is a browser group's share of access logs.
|
|
type BrowserShare = analyticsmodel.BrowserShare
|
|
|
|
// TopUser is an active user ranked by access count.
|
|
type TopUser = analyticsmodel.TopUser
|
|
|
|
// GetDailyTrend returns per-day access counts for the last days days (inclusive of today).
|
|
func GetDailyTrend(ctx context.Context, days int) ([]DailyTrend, error) {
|
|
if days < 1 {
|
|
days = 7
|
|
}
|
|
if err := userAccessLogConn(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
startTime := time.Now().AddDate(0, 0, -(days - 1)).Truncate(hoursInDay * time.Hour)
|
|
tableName := analyticsmodel.UserAccessLog{}.TableName()
|
|
query := fmt.Sprintf(`
|
|
SELECT toDate(created_at) AS date, count() AS count
|
|
FROM %s
|
|
WHERE created_at >= ?
|
|
GROUP BY date
|
|
ORDER BY date ASC
|
|
`, tableName)
|
|
|
|
rows, err := db.ChConn.Query(ctx, query, startTime)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get daily trend: %w", err)
|
|
}
|
|
defer func() { _ = rows.Close() }()
|
|
|
|
trendMap := make(map[string]uint64, days)
|
|
for i := range days {
|
|
dateStr := time.Now().AddDate(0, 0, -i).Format("2006-01-02")
|
|
trendMap[dateStr] = 0
|
|
}
|
|
for rows.Next() {
|
|
var (
|
|
date time.Time
|
|
count uint64
|
|
)
|
|
if err := rows.Scan(&date, &count); err != nil {
|
|
return nil, fmt.Errorf("scan daily trend row: %w", err)
|
|
}
|
|
trendMap[date.Format("2006-01-02")] = count
|
|
}
|
|
|
|
result := make([]DailyTrend, 0, days)
|
|
for i := days - 1; i >= 0; i-- {
|
|
dateStr := time.Now().AddDate(0, 0, -i).Format("2006-01-02")
|
|
result = append(result, DailyTrend{
|
|
Date: dateStr,
|
|
Count: trendMap[dateStr],
|
|
})
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// GetBrowserDistribution returns browser-grouped access counts since startTime.
|
|
func GetBrowserDistribution(ctx context.Context, startTime time.Time) ([]BrowserShare, error) {
|
|
if err := userAccessLogConn(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tableName := analyticsmodel.UserAccessLog{}.TableName()
|
|
query := fmt.Sprintf(`
|
|
SELECT user_agent, count() AS count
|
|
FROM %s
|
|
WHERE created_at >= ?
|
|
GROUP BY user_agent
|
|
ORDER BY count DESC
|
|
LIMIT 100
|
|
`, tableName)
|
|
|
|
rows, err := db.ChConn.Query(ctx, query, startTime)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get browser distribution: %w", err)
|
|
}
|
|
defer func() { _ = rows.Close() }()
|
|
|
|
browserCounts := make(map[string]uint64)
|
|
for rows.Next() {
|
|
var (
|
|
userAgent string
|
|
count uint64
|
|
)
|
|
if err := rows.Scan(&userAgent, &count); err != nil {
|
|
return nil, fmt.Errorf("scan browser distribution row: %w", err)
|
|
}
|
|
browser := ParseBrowserName(userAgent)
|
|
browserCounts[browser] += count
|
|
}
|
|
|
|
result := make([]BrowserShare, 0, len(browserCounts))
|
|
for browser, count := range browserCounts {
|
|
result = append(result, BrowserShare{
|
|
Browser: browser,
|
|
Count: count,
|
|
})
|
|
}
|
|
sort.Slice(result, func(i, j int) bool {
|
|
return result[i].Count > result[j].Count
|
|
})
|
|
return result, nil
|
|
}
|
|
|
|
// GetTopActiveUsers returns the most active users since startTime.
|
|
func GetTopActiveUsers(ctx context.Context, startTime time.Time, limit int) ([]TopUser, error) {
|
|
if limit < 1 {
|
|
limit = 10
|
|
}
|
|
if err := userAccessLogConn(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tableName := analyticsmodel.UserAccessLog{}.TableName()
|
|
query := fmt.Sprintf(`
|
|
SELECT user_id, count() AS count
|
|
FROM %s
|
|
WHERE created_at >= ? AND user_id > 0
|
|
GROUP BY user_id
|
|
ORDER BY count DESC
|
|
LIMIT ?
|
|
`, tableName)
|
|
|
|
rows, err := db.ChConn.Query(ctx, query, startTime, limit)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get top active users: %w", err)
|
|
}
|
|
defer func() { _ = rows.Close() }()
|
|
|
|
var users []TopUser
|
|
for rows.Next() {
|
|
var item TopUser
|
|
if err := rows.Scan(&item.UserID, &item.Count); err != nil {
|
|
return nil, fmt.Errorf("scan top active user row: %w", err)
|
|
}
|
|
users = append(users, item)
|
|
}
|
|
return users, nil
|
|
}
|