119 lines
4.4 KiB
Erlang
119 lines
4.4 KiB
Erlang
%%%-------------------------------------------------------------------
|
|
%%% @doc Административный обработчик подписок.
|
|
%%% GET – список с пагинацией и фильтрацией.
|
|
%%% @end
|
|
%%%-------------------------------------------------------------------
|
|
-module(admin_handler_subscriptions).
|
|
-behaviour(cowboy_handler).
|
|
-export([init/2]).
|
|
-export([trails/0]).
|
|
|
|
-include("records.hrl").
|
|
|
|
-spec init(cowboy_req:req(), any()) -> {ok, cowboy_req:req(), any()}.
|
|
init(Req, _Opts) ->
|
|
case cowboy_req:method(Req) of
|
|
<<"GET">> -> list_subscriptions(Req);
|
|
_ -> handler_utils:send_error(Req, 405, <<"Method not allowed">>)
|
|
end.
|
|
|
|
-spec trails() -> [map()].
|
|
trails() ->
|
|
[
|
|
#{
|
|
path => <<"/v1/admin/subscriptions">>,
|
|
method => <<"GET">>,
|
|
description => <<"List all subscriptions (admin)">>,
|
|
tags => [<<"Subscriptions">>],
|
|
parameters => [
|
|
#{name => <<"plan">>, in => <<"query">>, schema => #{type => string, enum => [<<"monthly">>, <<"quarterly">>, <<"biannual">>, <<"annual">>]}, description => <<"Filter by plan">>},
|
|
#{name => <<"status">>, in => <<"query">>, schema => #{type => string, enum => [<<"active">>, <<"expired">>, <<"cancelled">>]}, description => <<"Filter by status">>},
|
|
#{name => <<"limit">>, in => <<"query">>, schema => #{type => integer}, description => <<"Page size">>},
|
|
#{name => <<"offset">>, in => <<"query">>, schema => #{type => integer}, description => <<"Offset">>}
|
|
],
|
|
responses => #{
|
|
200 => #{
|
|
description => <<"Array of subscriptions">>,
|
|
content => #{<<"application/json">> => #{schema => #{
|
|
type => array,
|
|
items => subscription_schema()
|
|
}}}
|
|
}
|
|
}
|
|
}
|
|
].
|
|
|
|
subscription_schema() ->
|
|
#{
|
|
type => object,
|
|
properties => #{
|
|
id => #{type => string},
|
|
user_id => #{type => string},
|
|
plan => #{type => string, enum => [<<"monthly">>, <<"quarterly">>, <<"biannual">>, <<"annual">>]},
|
|
status => #{type => string, enum => [<<"active">>, <<"expired">>, <<"cancelled">>]},
|
|
trial_used => #{type => boolean},
|
|
started_at => #{type => string, format => <<"date-time">>},
|
|
expires_at => #{type => string, format => <<"date-time">>},
|
|
created_at => #{type => string, format => <<"date-time">>},
|
|
updated_at => #{type => string, format => <<"date-time">>}
|
|
}
|
|
}.
|
|
|
|
%%% Internal functions
|
|
|
|
list_subscriptions(Req) ->
|
|
case handler_utils:auth_admin(Req) of
|
|
{ok, _AdminId, Req1} ->
|
|
Filters = parse_subscription_filters(Req1),
|
|
Pagination = handler_utils:parse_pagination_params(Req1),
|
|
{ok, AllSubscriptions} = core_subscription:list_all(),
|
|
Filtered = apply_filters(AllSubscriptions, Filters),
|
|
Sorted = sort_subscriptions(Filtered, Pagination),
|
|
Total = length(Sorted),
|
|
Page = lists:sublist(Sorted, maps:get(offset, Pagination) + 1, maps:get(limit, Pagination)),
|
|
Json = [handler_utils:subscription_to_json(S) || S <- Page],
|
|
ExtraHeaders = handler_utils:pagination_headers(Pagination, Total),
|
|
handler_utils:send_json(Req1, 200, Json, ExtraHeaders);
|
|
{error, Code, Msg, Req1} ->
|
|
handler_utils:send_error(Req1, Code, Msg)
|
|
end.
|
|
|
|
parse_subscription_filters(Req) ->
|
|
Qs = cowboy_req:parse_qs(Req),
|
|
#{
|
|
plan => proplists:get_value(<<"plan">>, Qs),
|
|
status => proplists:get_value(<<"status">>, Qs)
|
|
}.
|
|
|
|
apply_filters(Subs, Filters) ->
|
|
PlanBin = maps:get(plan, Filters, undefined),
|
|
StatusBin = maps:get(status, Filters, undefined),
|
|
F1 = case PlanBin of
|
|
undefined -> Subs;
|
|
_ ->
|
|
Plan = try binary_to_existing_atom(PlanBin, utf8)
|
|
catch error:badarg -> PlanBin end,
|
|
[S || S <- Subs, S#subscription.plan =:= Plan]
|
|
end,
|
|
case StatusBin of
|
|
undefined -> F1;
|
|
_ ->
|
|
Status = try binary_to_existing_atom(StatusBin, utf8)
|
|
catch error:badarg -> StatusBin end,
|
|
[S || S <- F1, S#subscription.status =:= Status]
|
|
end.
|
|
|
|
sort_subscriptions(Subs, #{sort := Sort, order := Order}) ->
|
|
Field = binary_to_existing_atom(Sort, utf8),
|
|
lists:sort(
|
|
fun(A, B) ->
|
|
ValA = sub_field(A, Field),
|
|
ValB = sub_field(B, Field),
|
|
if Order == <<"asc">> -> ValA =< ValB;
|
|
true -> ValA >= ValB
|
|
end
|
|
end, Subs).
|
|
|
|
sub_field(#subscription{created_at = V}, created_at) -> V;
|
|
sub_field(#subscription{expires_at = V}, expires_at) -> V;
|
|
sub_field(_, _) -> undefined. |