Расширена статистика для разделов #22

This commit is contained in:
2026-05-28 13:49:08 +03:00
parent 2dac5e5102
commit 1986fd4694
20 changed files with 1189 additions and 71 deletions
@@ -0,0 +1,97 @@
%%%-------------------------------------------------------------------
%%% @doc Административный обработчик статистики по жалобам.
%%% GET /v1/admin/reports/stats возвращает общее количество,
%%% распределение по типам цели, статусам и топ-10 целей по жалобам.
%%% @end
%%%-------------------------------------------------------------------
-module(admin_handler_report_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_report_stats(Req);
_ -> handler_utils:send_error(Req, 405, <<"Method not allowed">>)
end.
%%% Swagger metadata
-spec trails() -> [map()].
trails() ->
[
#{
path => <<"/v1/admin/reports/stats">>,
method => <<"GET">>,
description => <<"Get detailed report statistics (total, by target type, by status, top 10 targets by reports)">>,
tags => [<<"Statistics">>],
responses => #{
200 => #{
description => <<"Report statistics object">>,
content => #{<<"application/json">> => #{schema => report_stats_schema()}}
},
403 => #{description => <<"Admin access required">>}
}
}
].
report_stats_schema() ->
#{
type => object,
properties => #{
<<"total_reports">> => #{type => integer, description => <<"Total number of reports">>},
<<"reports_by_target_type">> => #{
type => object,
description => <<"Number of reports grouped by target type (event, calendar, review)">>,
additionalProperties => #{type => integer}
},
<<"reports_by_status">> => #{
type => object,
description => <<"Number of reports grouped by status (pending, reviewed, dismissed)">>,
additionalProperties => #{type => integer}
},
<<"top_targets_by_reports">> => #{
type => array,
description => <<"Top 10 targets by number of reports">>,
items => #{
type => object,
properties => #{
<<"target_type">> => #{type => string},
<<"target_id">> => #{type => string},
<<"report_count">> => #{type => integer}
}
}
}
}
}.
%%% Internal functions
%% @doc GET /v1/admin/reports/stats статистика по жалобам.
-spec get_report_stats(cowboy_req:req()) -> {ok, binary(), cowboy_req:req()}.
get_report_stats(Req) ->
case handler_utils:auth_admin(Req) of
{ok, _AdminId, Req1} ->
Total = core_report:count_reports(),
ByTargetType = core_report:count_reports_by_target_type(),
ByStatus = core_report:count_reports_by_status(),
Top10 = core_report:get_top_targets_by_reports(10),
Stats = #{
<<"total_reports">> => Total,
<<"reports_by_target_type">> => maps:from_list([{atom_to_binary(Type, utf8), Count} || {Type, Count} <- ByTargetType]),
<<"reports_by_status">> => maps:from_list([{atom_to_binary(Status, utf8), Count} || {Status, Count} <- ByStatus]),
<<"top_targets_by_reports">> => lists:map(fun({TargetType, TargetId, Count}) ->
#{
<<"target_type">> => atom_to_binary(TargetType, utf8),
<<"target_id">> => TargetId,
<<"report_count">> => Count
}
end, Top10)
},
handler_utils:send_json(Req1, 200, Stats);
{error, Code, Msg, Req1} ->
handler_utils:send_error(Req1, Code, Msg)
end.