fix ticket stats

This commit is contained in:
2026-05-19 22:16:26 +03:00
parent d040256447
commit faee11ce29
4 changed files with 82 additions and 26 deletions
+47 -15
View File
@@ -30,12 +30,13 @@ update_ticket(Id, Updates) ->
delete_ticket(Id) ->
case get_by_id(Id) of
{ok, _Ticket} -> % переменная не используется
{ok, _Ticket} ->
mnesia:dirty_delete({ticket, Id}),
{ok, deleted};
Error -> Error
end.
%% @doc Статистика по тикетам (используется в admin_handler_ticket_stats)
stats() ->
Tickets = list_all(),
#{
@@ -50,6 +51,8 @@ stats() ->
create_ticket(Data) ->
Id = infra_utils:generate_id(9),
Now = calendar:universal_time(),
Status0 = maps:get(<<"status">>, Data, open), %% <-- ИСПРАВЛЕНО: извлекаем сырое значение
Status = normalize_status(Status0), %% <-- ИСПРАВЛЕНО: приводим к атому
Ticket = #ticket{
id = Id,
reporter_id = maps:get(<<"reporter_id">>, Data, undefined),
@@ -60,9 +63,10 @@ create_ticket(Data) ->
count = 1,
first_seen = Now,
last_seen = Now,
status = maps:get(<<"status">>, Data, open),
status = Status, %% <-- ИСПРАВЛЕНО: сохраняем атом
assigned_to = maps:get(<<"assigned_to">>, Data, undefined),
resolution_note = maps:get(<<"resolution_note">>, Data, undefined)
resolution_note = maps:get(<<"resolution_note">>, Data, undefined),
closed_at = undefined
},
mnesia:dirty_write(Ticket),
{ok, Ticket}.
@@ -70,32 +74,52 @@ create_ticket(Data) ->
list_by_user(UserId) ->
mnesia:dirty_match_object(#ticket{reporter_id = UserId, _ = '_'}).
%% ── функции подсчёта с нормализацией статуса ─────────────
%% @private Подсчитывает тикеты с заданным статусом (атом или бинарный)
count_by_status(Status, Tickets) ->
length([T || T <- Tickets, T#ticket.status =:= Status]).
length([T || T <- Tickets, normalize_status(T#ticket.status) =:= Status]).
%% @doc Количество тикетов по статусу (атом или бинарный)
count_tickets_by_status(Status) ->
Match = #ticket{status = Status, _ = '_'},
length(mnesia:dirty_match_object(Match)).
Tickets = list_all(),
count_by_status(Status, Tickets).
%% @doc Количество тикетов, назначенных администратору, с заданным статусом
count_tickets_by_admin(AdminId, Status) ->
Match = #ticket{assigned_to = AdminId, status = Status, _ = '_'},
length(mnesia:dirty_match_object(Match)).
Tickets = list_all(),
length([T || T <- Tickets,
T#ticket.assigned_to =:= AdminId andalso
normalize_status(T#ticket.status) =:= Status]).
avg_resolution_time() ->
Tickets = mnesia:dirty_match_object(#ticket{status = closed, _ = '_'}),
case Tickets of
% Загружаем все тикеты (или можно только закрытые, если их мало – решите по нагрузке)
Tickets = mnesia:dirty_match_object(#ticket{_ = '_'}),
% Фильтруем закрытые с учётом нормализации статуса
ClosedTickets = [T || T <- Tickets,
normalize_status(T#ticket.status) =:= closed,
T#ticket.closed_at =/= undefined],
case ClosedTickets of
[] -> 0;
_ ->
TotalSeconds = lists:sum([calendar:datetime_to_gregorian_seconds(T#ticket.last_seen) -
calendar:datetime_to_gregorian_seconds(T#ticket.first_seen) || T <- Tickets]),
TotalSeconds / length(Tickets) / 3600.0
TotalSeconds = lists:sum([
calendar:datetime_to_gregorian_seconds(T#ticket.closed_at) -
calendar:datetime_to_gregorian_seconds(T#ticket.first_seen)
|| T <- ClosedTickets]),
TotalSeconds / length(ClosedTickets) / 3600.0
end.
%% ── внутренние ─────────────────────────────────────────
apply_updates(Ticket, Updates) ->
lists:foldl(fun({Key, Value}, Acc) ->
case Key of
<<"status">> -> Acc#ticket{status = binary_to_atom(Value, utf8)};
<<"status">> ->
NewStatus = normalize_status(Value),
Acc1 = Acc#ticket{status = NewStatus},
case NewStatus of
closed -> Acc1#ticket{closed_at = calendar:universal_time()};
_ -> Acc1
end;
<<"assigned_to">> -> Acc#ticket{assigned_to = Value};
<<"resolution_note">> -> Acc#ticket{resolution_note = Value};
<<"error_message">> -> Acc#ticket{error_message = Value};
@@ -103,4 +127,12 @@ apply_updates(Ticket, Updates) ->
<<"context">> -> Acc#ticket{context = Value};
_ -> Acc
end
end, Ticket, maps:to_list(Updates)).
end, Ticket, maps:to_list(Updates)).
%% @private Преобразует бинарный статус в атом, если нужно.
%% Атомы возвращает без изменений.
normalize_status(Status) when is_atom(Status) -> Status;
normalize_status(Status) when is_binary(Status) ->
try binary_to_existing_atom(Status, utf8)
catch error:badarg -> Status
end.