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
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
column do
panel "最冷门活跃城市" do

View File

@ -22,20 +22,45 @@ class City < ApplicationRecord
scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) }
scope :by_popularity, -> {
if ActiveRecord::Base.connection.adapter_name.downcase == "sqlite"
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'")
.group("cities.id")
.select("cities.*, COUNT(ahoy_events.id) as visit_count")
.order("visit_count DESC")
# 在 City 模型中
scope :by_popularity, ->(period = :year, limit = 100) {
# 根据时间周期确定时间范围
time_range =
case period.to_sym
when :today
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
Time.current.beginning_of_year..Time.current.end_of_year
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("LEFT JOIN ahoy_events ON (ahoy_events.properties::jsonb->>'city_id')::integer = cities.id
AND ahoy_events.properties::jsonb->>'event_type' = 'city_view'")
.group("cities.id")
.select("cities.*, COUNT(ahoy_events.id) as visit_count")
.order("visit_count DESC")
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")
.select("cities.*, COUNT(ahoy_events.id) as visit_count")
.order("visit_count DESC")
.limit(limit)
}
scope :least_popular_active, ->(limit = 100) {