feat: add city popularity displays by time period

- Implement new scope in City model to fetch popular cities
- Query modified to consider different time periods: today, week, month, and year
- Update Ahoy Dashboard to show top cities for each time period

This change enhances the dashboard, providing insights into city popularity over varying durations, facilitating better data analysis.
This commit is contained in:
songtianlun 2025-02-12 13:28:47 +08:00
parent 34342a9678
commit 51d626a67f
2 changed files with 72 additions and 12 deletions

View File

@ -37,6 +37,41 @@ ActiveAdmin.register_page "Ahoy Dashboard" do
end end
end end
columns do
column do
panel "今日热门城市" do
table_for City.by_popularity(:today, 10) do
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
column("访问量") { |city| city.view_count }
end
end
end
column do
panel "本周热门城市" do
table_for City.by_popularity(:week, 10) do
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
column("访问量") { |city| city.view_count }
end
end
end
column do
panel "月度热门城市" do
table_for City.by_popularity(:month, 10) do
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
column("访问量") { |city| city.view_count }
end
end
end
column do
panel "年度热门城市" do
table_for City.by_popularity(:year, 10) do
column("城市") { |city| link_to(city.name, admin_city_path(city)) }
column("访问量") { |city| city.view_count }
end
end
end
end
columns do columns do
column do column do
panel "最冷门活跃城市" do panel "最冷门活跃城市" do

View File

@ -22,20 +22,45 @@ class City < ApplicationRecord
scope :active, -> { where(active: true) } scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) } scope :inactive, -> { where(active: false) }
scope :by_popularity, -> { # 在 City 模型中
if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite" scope :by_popularity, ->(period = :year, limit = 100) {
joins("LEFT JOIN ahoy_events ON json_extract(ahoy_events.properties, '$.city_id') = cities.id # 根据时间周期确定时间范围
AND json_extract(ahoy_events.properties, '$.event_type') = 'city_view'") time_range =
.group("cities.id") case period.to_sym
.select("cities.*, COUNT(ahoy_events.id) as visit_count") when :today
.order("visit_count DESC") Time.current.beginning_of_day..Time.current.end_of_day
when :week
Time.current.beginning_of_week..Time.current.end_of_week
when :month
Time.current.beginning_of_month..Time.current.end_of_month
when :year
Time.current.beginning_of_year..Time.current.end_of_year
else else
joins("LEFT JOIN ahoy_events ON (ahoy_events.properties::jsonb->>'city_id')::integer = cities.id Time.current.beginning_of_year..Time.current.end_of_year
AND ahoy_events.properties::jsonb->>'event_type' = 'city_view'") end
# 根据数据库类型构建不同的查询
base_query = if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite"
joins(<<-SQL.squish)
LEFT JOIN ahoy_events ON
json_extract(ahoy_events.properties, '$.city_id') = cities.id
AND json_extract(ahoy_events.properties, '$.event_type') = 'city_view'
AND ahoy_events.time BETWEEN '#{time_range.begin}' AND '#{time_range.end}'
SQL
else
joins(<<-SQL.squish)
LEFT JOIN ahoy_events ON
(ahoy_events.properties::jsonb->>'city_id')::integer = cities.id
AND ahoy_events.properties::jsonb->>'event_type' = 'city_view'
AND ahoy_events.time BETWEEN '#{time_range.begin}' AND '#{time_range.end}'
SQL
end
base_query
.group("cities.id") .group("cities.id")
.select("cities.*, COUNT(ahoy_events.id) as visit_count") .select("cities.*, COUNT(ahoy_events.id) as visit_count")
.order("visit_count DESC") .order("visit_count DESC")
end .limit(limit)
} }
scope :least_popular_active, ->(limit = 100) { scope :least_popular_active, ->(limit = 100) {