106 lines
3.6 KiB
Erlang
106 lines
3.6 KiB
Erlang
-module(core_ticket).
|
|
-include("records.hrl").
|
|
-export([list_all/0,
|
|
get_by_id/1,
|
|
update_ticket/2,
|
|
delete_ticket/1,
|
|
stats/0,
|
|
create_ticket/1,
|
|
list_by_user/1]).
|
|
-export([count_tickets_by_status/1, count_tickets_by_admin/2]).
|
|
-export([avg_resolution_time/0]).
|
|
|
|
list_all() ->
|
|
mnesia:dirty_match_object(#ticket{_ = '_'}).
|
|
|
|
get_by_id(Id) ->
|
|
case mnesia:dirty_read({ticket, Id}) of
|
|
[Ticket] -> {ok, Ticket};
|
|
[] -> {error, not_found}
|
|
end.
|
|
|
|
update_ticket(Id, Updates) ->
|
|
case get_by_id(Id) of
|
|
{ok, Ticket} ->
|
|
Updated = apply_updates(Ticket, Updates),
|
|
mnesia:dirty_write(Updated),
|
|
{ok, Updated};
|
|
Error -> Error
|
|
end.
|
|
|
|
delete_ticket(Id) ->
|
|
case get_by_id(Id) of
|
|
{ok, _Ticket} -> % переменная не используется
|
|
mnesia:dirty_delete({ticket, Id}),
|
|
{ok, deleted};
|
|
Error -> Error
|
|
end.
|
|
|
|
stats() ->
|
|
Tickets = list_all(),
|
|
#{
|
|
total => length(Tickets),
|
|
open => count_by_status(open, Tickets),
|
|
in_progress => count_by_status(in_progress, Tickets),
|
|
resolved => count_by_status(resolved, Tickets),
|
|
closed => count_by_status(closed, Tickets)
|
|
}.
|
|
|
|
%% ── новые функции ──────────────────────────────────────
|
|
create_ticket(Data) ->
|
|
Id = base64:encode(crypto:strong_rand_bytes(9), #{mode => urlsafe, padding => false}),
|
|
Now = calendar:universal_time(),
|
|
Ticket = #ticket{
|
|
id = Id,
|
|
reporter_id = maps:get(<<"reporter_id">>, Data, undefined),
|
|
error_hash = maps:get(<<"error_hash">>, Data, <<"">>),
|
|
error_message = maps:get(<<"error_message">>, Data),
|
|
stacktrace = maps:get(<<"stacktrace">>, Data, <<"">>),
|
|
context = maps:get(<<"context">>, Data, <<"">>),
|
|
count = 1,
|
|
first_seen = Now,
|
|
last_seen = Now,
|
|
status = maps:get(<<"status">>, Data, open),
|
|
assigned_to = maps:get(<<"assigned_to">>, Data, undefined),
|
|
resolution_note = maps:get(<<"resolution_note">>, Data, undefined)
|
|
},
|
|
mnesia:dirty_write(Ticket),
|
|
{ok, Ticket}.
|
|
|
|
list_by_user(UserId) ->
|
|
mnesia:dirty_match_object(#ticket{reporter_id = UserId, _ = '_'}).
|
|
|
|
count_by_status(Status, Tickets) ->
|
|
length([T || T <- Tickets, T#ticket.status =:= Status]).
|
|
|
|
count_tickets_by_status(Status) ->
|
|
Match = #ticket{status = Status, _ = '_'},
|
|
length(mnesia:dirty_match_object(Match)).
|
|
|
|
count_tickets_by_admin(AdminId, Status) ->
|
|
Match = #ticket{assigned_to = AdminId, status = Status, _ = '_'},
|
|
length(mnesia:dirty_match_object(Match)).
|
|
|
|
avg_resolution_time() ->
|
|
Tickets = mnesia:dirty_match_object(#ticket{status = closed, _ = '_'}),
|
|
case Tickets 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
|
|
end.
|
|
|
|
%% ── внутренние ─────────────────────────────────────────
|
|
apply_updates(Ticket, Updates) ->
|
|
lists:foldl(fun({Key, Value}, Acc) ->
|
|
case Key of
|
|
<<"status">> -> Acc#ticket{status = binary_to_atom(Value, utf8)};
|
|
<<"assigned_to">> -> Acc#ticket{assigned_to = Value};
|
|
<<"resolution_note">> -> Acc#ticket{resolution_note = Value};
|
|
<<"error_message">> -> Acc#ticket{error_message = Value};
|
|
<<"stacktrace">> -> Acc#ticket{stacktrace = Value};
|
|
<<"context">> -> Acc#ticket{context = Value};
|
|
_ -> Acc
|
|
end
|
|
end, Ticket, maps:to_list(Updates)). |