diff --git a/app/controllers/api/v3/users_controller.rb b/app/controllers/api/v3/users_controller.rb index 69648600c3..d26b8c6dd1 100644 --- a/app/controllers/api/v3/users_controller.rb +++ b/app/controllers/api/v3/users_controller.rb @@ -17,7 +17,7 @@ def index limit = params[:limit].to_i limit = 100 if limit > 100 - @users = User.fields_for_list.hot.limit(limit) + @users = @active_users = Counter.where(countable_type: "User", key: "yearly_replies_count").includes(:countable).order("value desc").limit(limit).map(&:countable) end # Get full detail of current user, for account setting. diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 90f4a3d6be..9255117b7a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -13,7 +13,8 @@ class UsersController < ApplicationController def index @total_user_count = User.count - @active_users = User.without_team.fields_for_list.hot.limit(100) + key = params[:type] = "monthly" ? :monthly_replies_count : :yearly_replies_count + @active_users = Counter.where(countable_type: "User", key: key).includes(:countable).order("value desc").limit(100).map(&:countable) end def feed diff --git a/app/jobs/scheduler/total_user_stats_job.rb b/app/jobs/scheduler/total_user_stats_job.rb new file mode 100644 index 0000000000..c322f64f3d --- /dev/null +++ b/app/jobs/scheduler/total_user_stats_job.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Scheduler + # cleanup spam topic at 1 month ago + class TotalUserStatsJob < ApplicationJob + def perform(limit: 2000) + users = User.where("replies_count > 0") + users = if limit.to_i > 0 + users.order("updated_at desc").limit(limit) + else + users.where("replies_count > 0").order("updated_at desc").find_each + end + + users.each do |user| + user.monthly_replies_count.update(value: user.replies.where("created_at > ?", 1.month.ago).count) + user.yearly_replies_count.update(value: user.replies.where("created_at > ?", 1.year.ago).count) + end + end + end +end diff --git a/app/models/application_record.rb b/app/models/application_record.rb index f1b8f84881..95feacf56e 100644 --- a/app/models/application_record.rb +++ b/app/models/application_record.rb @@ -3,7 +3,8 @@ class ApplicationRecord < ActiveRecord::Base self.abstract_class = true - include Counterable + include RedisCountable + include Countable scope :recent, -> { order(id: :desc) } scope :exclude_ids, ->(ids) { where.not(id: ids.map(&:to_i)) } diff --git a/app/models/concerns/countable.rb b/app/models/concerns/countable.rb new file mode 100644 index 0000000000..f947971149 --- /dev/null +++ b/app/models/concerns/countable.rb @@ -0,0 +1,25 @@ +# Countable for storage by use ActiveRecord +module Countable + extend ActiveSupport::Concern + + included do + scope :with_counters, -> { includes(@@countable_names) } + end + + class_methods do + def countable(*names) + class_eval do + @@countable_names ||= [] + + names.each do |name| + @@countable_names << "#{name}_counter".to_sym + has_one :"#{name}_counter", -> { where(key: name) }, as: :countable, class_name: "Counter" + + define_method :"#{name}" do + send(:"#{name}_counter") || send(:"create_#{name}_counter") + end + end + end + end + end +end diff --git a/app/models/concerns/counterable.rb b/app/models/concerns/redis_countable.rb similarity index 97% rename from app/models/concerns/counterable.rb rename to app/models/concerns/redis_countable.rb index e94b7f3764..bb3879b1b7 100644 --- a/app/models/concerns/counterable.rb +++ b/app/models/concerns/redis_countable.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # 增加访问量的功能 -module Counterable +module RedisCountable extend ActiveSupport::Concern class Counter diff --git a/app/models/counter.rb b/app/models/counter.rb new file mode 100644 index 0000000000..7fc632a58a --- /dev/null +++ b/app/models/counter.rb @@ -0,0 +1,22 @@ +class Counter < ApplicationRecord + belongs_to :countable, polymorphic: true + validates :countable, presence: true + + delegate :to_i, :to_s, :inspect, to: :value + + def incr(by = 1) + increment!(:value, by).value + end + + def decr(by = 1) + decrement!(:value, by).value + end + + def method_missing(method, *args, &block) + if value.respond_to?(method) + value.send(method, *args, &block) + else + super + end + end +end diff --git a/app/models/user.rb b/app/models/user.rb index e9eb80e9bc..9eeb5c39df 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -36,6 +36,8 @@ class User < ApplicationRecord has_many :teams, through: :team_users has_one :sso, class_name: "UserSSO", dependent: :destroy + countable :monthly_replies_count, :yearly_replies_count + attr_accessor :password_confirmation validates :login, format: {with: ALLOW_LOGIN_FORMAT_REGEXP, message: I18n.t("users.username_allows_format")}, @@ -62,11 +64,11 @@ def self.find_for_database_authentication(warden_conditions) end def self.find_by_email(email) - fetch_by_uniq_keys(email: email) + fetch_by_uniq_keys(email:) end def self.find_by_login!(slug) - find_by_login(slug) || raise(ActiveRecord::RecordNotFound.new(slug: slug)) + find_by_login(slug) || raise(ActiveRecord::RecordNotFound.new(slug:)) end def self.find_by_login(slug) diff --git a/app/views/users/_sidebar.html.erb b/app/views/users/_sidebar.html.erb index 85e03519ed..c8143354ca 100644 --- a/app/views/users/_sidebar.html.erb +++ b/app/views/users/_sidebar.html.erb @@ -25,6 +25,7 @@