diff --git a/app/controllers/api/v2/rgbpp_assets_statistics_controller.rb b/app/controllers/api/v2/rgbpp_assets_statistics_controller.rb new file mode 100644 index 000000000..ac8303481 --- /dev/null +++ b/app/controllers/api/v2/rgbpp_assets_statistics_controller.rb @@ -0,0 +1,24 @@ +module Api + module V2 + class RgbppAssetsStatisticsController < BaseController + def index + expires_in 15.minutes, public: true, stale_while_revalidate: 5.minutes, stale_if_error: 5.minutes + + statistics = RgbppAssetsStatistic.all.order(created_at_unixtimestamp: :asc) + statistics = statistics.where(network: params[:network]) if params[:network].present? + statistics = statistics.where(indicator: params[:indicators].split(",")) if params[:indicators].present? + + render json: { + data: statistics.map do |statistic| + { + indicator: statistic.indicator, + value: statistic.value.to_s, + network: statistic.network, + created_at_unixtimestamp: statistic.created_at_unixtimestamp.to_s, + } + end, + } + end + end + end +end diff --git a/app/controllers/api/v2/rgbpp_hourly_statistics_controller.rb b/app/controllers/api/v2/rgbpp_hourly_statistics_controller.rb deleted file mode 100644 index f10ac2cf5..000000000 --- a/app/controllers/api/v2/rgbpp_hourly_statistics_controller.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Api - module V2 - class RgbppHourlyStatisticsController < BaseController - def index - expires_in 15.minutes, public: true, stale_while_revalidate: 5.minutes, stale_if_error: 5.minutes - - rgbpp_statistics = RgbppHourlyStatistic.order(created_at_unixtimestamp: :asc) - - render json: { - data: rgbpp_statistics.map do |statistic| - { - xudt_count: statistic.xudt_count.to_s, - dob_count: statistic.dob_count.to_s, - created_at_unixtimestamp: statistic.created_at_unixtimestamp.to_s, - } - end, - } - end - end - end -end diff --git a/app/models/rgbpp_assets_statistic.rb b/app/models/rgbpp_assets_statistic.rb new file mode 100644 index 000000000..71b5249f9 --- /dev/null +++ b/app/models/rgbpp_assets_statistic.rb @@ -0,0 +1,21 @@ +class RgbppAssetsStatistic < ApplicationRecord + enum :network, %i[global ckb btc] + enum :indicator, %i[ft_count dob_count holders_count transactions_count] +end + +# == Schema Information +# +# Table name: rgbpp_assets_statistics +# +# id :bigint not null, primary key +# indicator :integer not null +# value :decimal(40, ) default(0) +# network :integer default("global") +# created_at_unixtimestamp :integer +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_on_indicator_and_network_and_created_at_unixtimestamp (indicator,network,created_at_unixtimestamp) UNIQUE +# diff --git a/app/workers/generate_rgbpp_assets_statistic_worker.rb b/app/workers/generate_rgbpp_assets_statistic_worker.rb new file mode 100644 index 000000000..badc1b99e --- /dev/null +++ b/app/workers/generate_rgbpp_assets_statistic_worker.rb @@ -0,0 +1,79 @@ +class GenerateRgbppAssetsStatisticWorker + include Sidekiq::Job + sidekiq_options queue: "rgbpp" + + def perform + statistic_attributes = [ + ft_count_attributes, + dob_count_attributes, + btc_transactions_count_attributes, + ckb_transactions_count_attributes, + btc_holders_count_attributes, + ckb_holders_count_attributes, + ] + + puts "====" + puts statistic_attributes + statistic_attributes.each { _1[:created_at_unixtimestamp] = started_at.to_i } + RgbppAssetsStatistic.upsert_all(statistic_attributes, unique_by: %i[indicator network created_at_unixtimestamp]) + rescue StandardError => e + Rails.logger.error "Error occurred during GenerateRgbppHourlyStatistic error: #{e.message}" + end + + private + + def ft_count_attributes + xudts_count = Udt.published_xudt.joins(:xudt_tag).where("xudt_tags.tags && ARRAY[?]::varchar[]", ["rgb++"]).count + { indicator: "ft_count", value: xudts_count, network: "global" } + end + + def dob_count_attributes + token_collections_count = TokenCollection.where("tags && ARRAY[?]::varchar[]", ["rgb++"]).count + { indicator: "dob_count", value: token_collections_count, network: "global" } + end + + def btc_transactions_count_attributes + transactions_count = BitcoinTransaction.where(time: started_at.to_i..ended_at.to_i).count + { indicator: "transactions_count", value: transactions_count, network: "btc" } + end + + def ckb_transactions_count_attributes + started_timestamp = CkbUtils.time_in_milliseconds(started_at) + ended_timestamp = CkbUtils.time_in_milliseconds(ended_at) - 1 + transactions_count = BitcoinAnnotation.includes(:ckb_transaction). + where(ckb_transactions: { block_timestamp: started_timestamp..ended_timestamp }).count + { indicator: "transactions_count", value: transactions_count, network: "ckb" } + end + + def btc_holders_count_attributes + udt_types = %i[xudt xudt_compatible spore_cell did_cell] + udt_ids = Udt.where(udt_type: udt_types, published: true).ids + address_ids = UdtAccount.where(udt_id: udt_ids).where("amount > 0").pluck(:address_id).uniq + holders_count = BitcoinAddressMapping.where(ckb_address_id: address_ids).distinct.count(:bitcoin_address_id) + { indicator: "holders_count", value: holders_count, network: "btc" } + end + + def ckb_holders_count_attributes + udt_types = %i[xudt xudt_compatible spore_cell did_cell] + udt_ids = Udt.where(udt_type: udt_types, published: true).ids + holders_count = UdtAccount.where(udt_id: udt_ids).where("amount > 0").distinct.count(:address_id) + { indicator: "holders_count", value: holders_count, network: "ckb" } + end + + def to_be_counted_date + last_record = UdtHourlyStatistic.order(created_at_unixtimestamp: :desc).first + if last_record + Time.zone.at(last_record.created_at_unixtimestamp) + 1.day + else + Time.current.yesterday + end + end + + def started_at + @started_at ||= to_be_counted_date.beginning_of_day + end + + def ended_at + @ended_at ||= to_be_counted_date.end_of_day + end +end diff --git a/app/workers/generate_rgbpp_hourly_statistic_worker.rb b/app/workers/generate_rgbpp_hourly_statistic_worker.rb deleted file mode 100644 index b61a25c47..000000000 --- a/app/workers/generate_rgbpp_hourly_statistic_worker.rb +++ /dev/null @@ -1,26 +0,0 @@ -class GenerateRgbppHourlyStatisticWorker - include Sidekiq::Job - - def perform - xudt_count = Udt.published_xudt.joins(:xudt_tag).where("xudt_tags.tags && ARRAY[?]::varchar[]", ["rgb++"]).count - dob_count = TokenCollection.where("tags && ARRAY[?]::varchar[]", ["rgb++"]).count - created_at_unixtimestamp = to_be_counted_date.beginning_of_day.to_i - RgbppHourlyStatistic.upsert( - { xudt_count:, dob_count:, created_at_unixtimestamp: }, - unique_by: :created_at_unixtimestamp, - ) - rescue StandardError => e - Rails.logger.error "Error occurred during GenerateRgbppHourlyStatistic error: #{e.message}" - end - - private - - def to_be_counted_date - last_record = UdtHourlyStatistic.order(created_at_unixtimestamp: :desc).first - if last_record - Time.zone.at(last_record.created_at_unixtimestamp) + 1.day - else - Time.current.yesterday - end - end -end diff --git a/config/routes/v2.rb b/config/routes/v2.rb index 247c8d9c6..a72b59b95 100644 --- a/config/routes/v2.rb +++ b/config/routes/v2.rb @@ -107,6 +107,6 @@ resources :graph_channels, only: :index end resources :udt_hourly_statistics, only: :show - resources :rgbpp_hourly_statistics, only: :index + resources :rgbpp_assets_statistics, only: :index end end diff --git a/db/migrate/20241218085721_create_rgbpp_assets_statistics.rb b/db/migrate/20241218085721_create_rgbpp_assets_statistics.rb new file mode 100644 index 000000000..a14b32422 --- /dev/null +++ b/db/migrate/20241218085721_create_rgbpp_assets_statistics.rb @@ -0,0 +1,15 @@ +class CreateRgbppAssetsStatistics < ActiveRecord::Migration[7.0] + def change + create_table :rgbpp_assets_statistics do |t| + t.integer :indicator, null: false + t.decimal :value, precision: 40, default: 0.0 + t.integer :network, default: 0 + t.integer :created_at_unixtimestamp + + t.timestamps + end + + add_index :rgbpp_assets_statistics, %i[indicator network created_at_unixtimestamp], unique: true, + name: "index_on_indicator_and_network_and_created_at_unixtimestamp" + end +end diff --git a/db/structure.sql b/db/structure.sql index fa548b2fd..db42dedbe 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -2344,6 +2344,40 @@ CREATE SEQUENCE public.reject_reasons_id_seq ALTER SEQUENCE public.reject_reasons_id_seq OWNED BY public.reject_reasons.id; +-- +-- Name: rgbpp_assets_statistics; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.rgbpp_assets_statistics ( + id bigint NOT NULL, + indicator integer NOT NULL, + value numeric(40,0) DEFAULT 0.0, + network integer DEFAULT 0, + created_at_unixtimestamp integer, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: rgbpp_assets_statistics_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.rgbpp_assets_statistics_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: rgbpp_assets_statistics_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.rgbpp_assets_statistics_id_seq OWNED BY public.rgbpp_assets_statistics.id; + + -- -- Name: rgbpp_hourly_statistics; Type: TABLE; Schema: public; Owner: - -- @@ -3369,6 +3403,13 @@ ALTER TABLE ONLY public.portfolios ALTER COLUMN id SET DEFAULT nextval('public.p ALTER TABLE ONLY public.reject_reasons ALTER COLUMN id SET DEFAULT nextval('public.reject_reasons_id_seq'::regclass); +-- +-- Name: rgbpp_assets_statistics id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rgbpp_assets_statistics ALTER COLUMN id SET DEFAULT nextval('public.rgbpp_assets_statistics_id_seq'::regclass); + + -- -- Name: rgbpp_hourly_statistics id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3952,6 +3993,14 @@ ALTER TABLE ONLY public.reject_reasons ADD CONSTRAINT reject_reasons_pkey PRIMARY KEY (id); +-- +-- Name: rgbpp_assets_statistics rgbpp_assets_statistics_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.rgbpp_assets_statistics + ADD CONSTRAINT rgbpp_assets_statistics_pkey PRIMARY KEY (id); + + -- -- Name: rgbpp_hourly_statistics rgbpp_hourly_statistics_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -5156,6 +5205,13 @@ CREATE UNIQUE INDEX index_omiga_inscription_infos_on_udt_hash ON public.omiga_in CREATE INDEX index_on_cell_dependencies_contract_cell_block_tx ON public.cell_dependencies USING btree (contract_cell_id, block_number DESC, tx_index DESC); +-- +-- Name: index_on_indicator_and_network_and_created_at_unixtimestamp; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX index_on_indicator_and_network_and_created_at_unixtimestamp ON public.rgbpp_assets_statistics USING btree (indicator, network, created_at_unixtimestamp); + + -- -- Name: index_on_udt_id_and_unixtimestamp; Type: INDEX; Schema: public; Owner: - -- @@ -6270,6 +6326,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20241202072604'), ('20241205023729'), ('20241212022531'), -('20241213053309'); +('20241213053309'), +('20241218085721'); diff --git a/lib/scheduler.rb b/lib/scheduler.rb index d1dd33d5a..b9006f311 100644 --- a/lib/scheduler.rb +++ b/lib/scheduler.rb @@ -33,7 +33,7 @@ def call_worker(clz) s.cron "0 8 * * *" do call_worker Charts::DailyStatistic call_worker GenerateUdtHourlyStatisticWorker - call_worker GenerateRgbppHourlyStatisticWorker + call_worker GenerateRgbppAssetsStatisticWorker end s.every "10m", overlap: false do