Files
EventHubBack/src/handlers/admin/admin_handler_subscription_stats.erl
T

102 lines
4.1 KiB
Erlang
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
%%%-------------------------------------------------------------------
%%% @doc Административный обработчик статистики по подпискам.
%%% GET /v1/admin/subscriptions/stats возвращает общее количество,
%%% распределение по планам, статусам, пробные, заканчивающиеся платные.
%%% @end
%%%-------------------------------------------------------------------
-module(admin_handler_subscription_stats).
-behaviour(cowboy_handler).
-export([init/2]).
-export([trails/0]).
-include("records.hrl").
%%% cowboy_handler callback
-spec init(cowboy_req:req(), any()) -> {ok, cowboy_req:req(), any()}.
init(Req, _Opts) ->
case cowboy_req:method(Req) of
<<"GET">> -> get_subscription_stats(Req);
_ -> handler_utils:send_error(Req, 405, <<"Method not allowed">>)
end.
%%% Swagger metadata
-spec trails() -> [map()].
trails() ->
[
#{
path => <<"/v1/admin/subscriptions/stats">>,
method => <<"GET">>,
description => <<"Get detailed subscription statistics (total, by plan, by status, trial count, ending paid subscriptions)">>,
tags => [<<"Statistics">>],
responses => #{
200 => #{
description => <<"Subscription statistics object">>,
content => #{<<"application/json">> => #{schema => subscription_stats_schema()}}
},
403 => #{description => <<"Admin access required">>}
}
}
].
subscription_stats_schema() ->
#{
type => object,
properties => #{
<<"total_subscriptions">> => #{type => integer, description => <<"Total number of subscriptions">>},
<<"subscriptions_by_plan">> => #{
type => object,
description => <<"Number of subscriptions grouped by plan">>,
additionalProperties => #{type => integer}
},
<<"subscriptions_by_status">> => #{
type => object,
description => <<"Number of subscriptions grouped by status">>,
additionalProperties => #{type => integer}
},
<<"trial_subscriptions">> => #{type => integer, description => <<"Number of subscriptions in trial period (trial_used = false)">>},
<<"ending_paid_subscriptions">> => #{
type => array,
description => <<"Paid subscriptions expiring within 30 days">>,
items => #{
type => object,
properties => #{
<<"id">> => #{type => string},
<<"user_id">> => #{type => string},
<<"plan">> => #{type => string},
<<"expires_at">> => #{type => string, format => <<"date-time">>}
}
}
}
}
}.
%%% Internal functions
%% @doc GET /v1/admin/subscriptions/stats статистика по подпискам.
-spec get_subscription_stats(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}.
get_subscription_stats(Req) ->
case handler_utils:auth_admin(Req) of
{ok, _AdminId, Req1} ->
Total = core_subscription:count_subscriptions(),
ByPlan = core_subscription:count_subscriptions_by_plan(),
ByStatus = core_subscription:count_subscriptions_by_status(),
Trial = core_subscription:count_trial_subscriptions(),
EndingPaid = core_subscription:get_ending_paid_subscriptions(30),
Stats = #{
<<"total_subscriptions">> => Total,
<<"subscriptions_by_plan">> => maps:from_list([{atom_to_binary(Plan, utf8), Count} || {Plan, Count} <- ByPlan]),
<<"subscriptions_by_status">> => maps:from_list([{atom_to_binary(Status, utf8), Count} || {Status, Count} <- ByStatus]),
<<"trial_subscriptions">> => Trial,
<<"ending_paid_subscriptions">> => lists:map(fun(Sub) ->
#{
<<"id">> => Sub#subscription.id,
<<"user_id">> => Sub#subscription.user_id,
<<"plan">> => atom_to_binary(Sub#subscription.plan, utf8),
<<"expires_at">> => handler_utils:datetime_to_iso8601(Sub#subscription.expires_at)
}
end, EndingPaid)
},
handler_utils:send_json(Req1, 200, Stats);
{error, Code, Msg, Req1} ->
handler_utils:send_error(Req1, Code, Msg)
end.