-module(logic_ticket). -include("records.hrl"). -export([report_error/3, get_ticket/2, list_tickets/1, list_tickets_by_status/2, update_status/3, assign_ticket/3, resolve_ticket/3, close_ticket/2, get_statistics/1]). -export([delete_ticket/2]). -export([get_user_ticket/2]). %% Зарегистрировать ошибку (создать или обновить тикет) report_error(ErrorMessage, Stacktrace, Context) -> Existing = [T || T <- core_ticket:list_all(), T#ticket.error_message =:= ErrorMessage], case Existing of [Ticket] -> % Увеличить счётчик и обновить last_seen Updated = Ticket#ticket{ count = Ticket#ticket.count + 1, last_seen = calendar:universal_time() }, mnesia:dirty_write(Updated), {ok, Updated}; [] -> ReporterId = maps:get(<<"reporter_id">>, Context, maps:get(reporter_id, Context, <<"anonymous">>)), Data = #{ <<"reporter_id">> => ReporterId, <<"error_message">> => ErrorMessage, <<"stacktrace">> => Stacktrace, <<"context">> => list_to_binary(io_lib:format("~p", [Context])) }, case core_ticket:create_ticket(Data) of {ok, Ticket} = Result -> % Уведомление администраторов (заглушка) notify_admins(Ticket), Result; Error -> Error end end. %% Получить тикет, проверяя, что пользователь является репортером get_user_ticket(UserId, TicketId) -> case core_ticket:get_by_id(TicketId) of % используем базовую функцию без проверки прав {ok, Ticket} -> case Ticket#ticket.reporter_id =:= UserId of true -> {ok, Ticket}; false -> {error, access_denied} end; Error -> Error end. %% Получить тикет (только для админов) get_ticket(AdminId, TicketId) -> case admin_utils:is_admin(AdminId) of true -> core_ticket:get_by_id(TicketId); false -> {error, access_denied} end. %% Список всех тикетов (только для админов) list_tickets(AdminId) -> case admin_utils:is_admin(AdminId) of true -> core_ticket:list_all(); false -> {error, access_denied} end. %% Список тикетов по статусу (только для админов) list_tickets_by_status(AdminId, Status) -> case admin_utils:is_admin(AdminId) of true -> All = core_ticket:list_all(), [T || T <- All, T#ticket.status =:= Status]; false -> {error, access_denied} end. %% Обновить статус тикета update_status(AdminId, TicketId, closed) -> close_ticket(AdminId, TicketId); update_status(AdminId, TicketId, Status) -> case admin_utils:is_admin(AdminId) of true -> core_ticket:update_ticket(TicketId, #{<<"status">> => Status}); false -> {error, access_denied} end. %% Назначить тикет администратору assign_ticket(AdminId, TicketId, AssignToId) -> case admin_utils:is_admin(AdminId) of true -> core_ticket:update_ticket(TicketId, #{<<"assigned_to">> => AssignToId}); false -> {error, access_denied} end. %% Отметить тикет как решённый с примечанием resolve_ticket(AdminId, TicketId, ResolutionNote) -> case admin_utils:is_admin(AdminId) of true -> core_ticket:update_ticket(TicketId, #{ <<"status">> => <<"resolved">>, <<"resolution_note">> => ResolutionNote, <<"closed_at">> => calendar:universal_time() }); false -> {error, access_denied} end. %% Закрыть тикет close_ticket(AdminId, TicketId) -> case admin_utils:is_admin(AdminId) of true -> core_ticket:close(TicketId, AdminId); false -> {error, access_denied} end. %% Удалить тикет (только для админов) delete_ticket(AdminId, TicketId) -> case admin_utils:is_admin(AdminId) of true -> core_ticket:delete_ticket(TicketId); false -> {error, access_denied} end. %% Получить статистику по тикетам get_statistics(AdminId) -> case admin_utils:is_admin(AdminId) of true -> All = core_ticket:list_all(), Open = count_by_status(All, open), InProgress = count_by_status(All, in_progress), Resolved = count_by_status(All, resolved), Closed = count_by_status(All, closed), TotalErrors = lists:sum([T#ticket.count || T <- All]), #{ total_tickets => length(All), open => Open, in_progress => InProgress, resolved => Resolved, closed => Closed, total_errors => TotalErrors }; false -> {error, access_denied} end. count_by_status(Tickets, Status) -> length([T || T <- Tickets, normalize_status(T#ticket.status) =:= Status]). 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. %% ============ Вспомогательные функции ============ notify_admins(_Ticket) -> ok.