Рефакторинг админских обработчиков - пагинация, сортировка, фильтрация. Часть 1

This commit is contained in:
2026-05-26 13:04:31 +03:00
parent 01dbc6d6cb
commit ed5b275efb
24 changed files with 1009 additions and 1126 deletions
@@ -5,7 +5,6 @@
%%%-------------------------------------------------------------------
-module(admin_handler_subscriptions).
-behaviour(cowboy_handler).
-export([init/2]).
-export([trails/0]).
@@ -15,28 +14,28 @@
init(Req, _Opts) ->
case cowboy_req:method(Req) of
<<"GET">> -> list_subscriptions(Req);
_ -> handler_utils:send_error(Req, 405, <<"Method not allowed">>)
_ -> handler_utils:send_error(Req, 405, <<"Method not allowed">>)
end.
-spec trails() -> [map()].
trails() ->
[
#{
path => <<"/v1/admin/subscriptions">>,
method => <<"GET">>,
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">>},
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 => <<"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,
type => array,
items => subscription_schema()
}}}
}
@@ -46,17 +45,17 @@ trails() ->
subscription_schema() ->
#{
type => object,
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">>}
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">>}
}
}.
@@ -73,7 +72,7 @@ list_subscriptions(Req) ->
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 = pagination_headers(Pagination, Total),
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)
@@ -82,7 +81,7 @@ list_subscriptions(Req) ->
parse_subscription_filters(Req) ->
Qs = cowboy_req:parse_qs(Req),
#{
plan => proplists:get_value(<<"plan">>, Qs),
plan => proplists:get_value(<<"plan">>, Qs),
status => proplists:get_value(<<"status">>, Qs)
}.
@@ -117,12 +116,4 @@ sort_subscriptions(Subs, #{sort := Sort, order := Order}) ->
sub_field(#subscription{created_at = V}, created_at) -> V;
sub_field(#subscription{expires_at = V}, expires_at) -> V;
sub_field(_, _) -> undefined.
pagination_headers(#{limit := Limit, offset := Offset}, Total) ->
RangeEnd = min(Offset + Limit - 1, Total - 1),
#{
<<"content-range">> => iolist_to_binary(io_lib:format("items ~B-~B/~B", [Offset, RangeEnd, Total])),
<<"x-total-count">> => integer_to_binary(Total),
<<"access-control-expose-headers">> => <<"Content-Range, X-Total-Count">>
}.
sub_field(_, _) -> undefined.