Пагинация, фильтры и сортировки Часть 1 #20
This commit is contained in:
@@ -6,6 +6,9 @@
|
||||
-export([validate_event_time/1, validate_event_time/2, get_occurrences/3, cancel_occurrence/3]).
|
||||
-export([materialize_for_booking/3]).
|
||||
-export([list_all_events/1, get_event_admin/1, update_event_admin/2, delete_event_admin/1]).
|
||||
-export([search_events/1]).
|
||||
|
||||
-define(DEFAULT_SEARCH_DAYS, 30).
|
||||
|
||||
%% Создание одиночного события
|
||||
create_event(UserId, CalendarId, Title, StartTime, Duration) ->
|
||||
@@ -168,6 +171,112 @@ delete_event(UserId, EventId) ->
|
||||
Error
|
||||
end.
|
||||
|
||||
%% @doc Поиск событий с пагинацией, фильтрацией и сортировкой
|
||||
search_events(Params) ->
|
||||
#{
|
||||
from := FromIn, to := ToIn,
|
||||
status := StatusFilter,
|
||||
calendar_id := CalId,
|
||||
title := TitleExact,
|
||||
q := Query,
|
||||
limit := Limit,
|
||||
offset := Offset,
|
||||
sort := SortField,
|
||||
order := Order
|
||||
} = Params,
|
||||
{From, To} = ensure_time_range(FromIn, ToIn),
|
||||
%% 1. Получаем исходный список событий
|
||||
AllEvents = case CalId of
|
||||
undefined ->
|
||||
% Без календаря — только активные мастер-события
|
||||
core_event:list_all();
|
||||
_ ->
|
||||
% Для конкретного календаря загружаем все события (любой статус)
|
||||
mnesia:dirty_index_match_object(
|
||||
event,
|
||||
#event{calendar_id = CalId, _ = '_'},
|
||||
calendar_id
|
||||
)
|
||||
end,
|
||||
%% 2. Фильтрация по дате (игнорируем undefined и instance)
|
||||
TimeFiltered = [E || E <- AllEvents,
|
||||
E#event.is_instance =:= false,
|
||||
E#event.start_time =/= undefined,
|
||||
E#event.start_time >= From,
|
||||
E#event.start_time =< To],
|
||||
%% 3. Фильтр по точному названию
|
||||
TitleFiltered = if TitleExact /= undefined ->
|
||||
[E || E <- TimeFiltered, E#event.title =:= TitleExact];
|
||||
true -> TimeFiltered
|
||||
end,
|
||||
%% 4. Пост‑фильтры по статусу и поисковой строке
|
||||
%% Определяем эффективный фильтр статуса
|
||||
EffectiveStatus = case {CalId, StatusFilter} of
|
||||
{undefined, undefined} -> undefined; % без календаря → только active
|
||||
{_, undefined} -> <<"all">>; % конкретный календарь → все статусы
|
||||
_ -> StatusFilter
|
||||
end,
|
||||
FinalFiltered = apply_post_filters(TitleFiltered, EffectiveStatus, Query),
|
||||
%% 5. Сортировка и пагинация
|
||||
SortedEvents = sort_events(FinalFiltered, SortField, Order),
|
||||
Total = length(SortedEvents),
|
||||
Page = lists:sublist(SortedEvents, Offset + 1, Limit),
|
||||
{ok, Total, Page}.
|
||||
|
||||
%% Дополнительная фильтрация по статусу и подстроке
|
||||
apply_post_filters(Events, StatusFilter, Query) ->
|
||||
E1 = case StatusFilter of
|
||||
<<"all">> -> Events;
|
||||
undefined -> [E || E <- Events, E#event.status =:= active];
|
||||
_ ->
|
||||
try binary_to_existing_atom(StatusFilter, utf8) of
|
||||
Atom -> [E || E <- Events, E#event.status =:= Atom]
|
||||
catch
|
||||
error:badarg -> Events
|
||||
end
|
||||
end,
|
||||
case Query of
|
||||
undefined -> E1;
|
||||
_ -> [E || E <- E1,
|
||||
string:str(binary_to_list(E#event.title), binary_to_list(Query)) > 0 orelse
|
||||
string:str(binary_to_list(E#event.description), binary_to_list(Query)) > 0]
|
||||
end.
|
||||
|
||||
%%--------------------------------------------------------------------
|
||||
%% Внутренние функции для search_events
|
||||
%%--------------------------------------------------------------------
|
||||
ensure_time_range(undefined, undefined) ->
|
||||
NowSec = calendar:datetime_to_gregorian_seconds(calendar:universal_time()),
|
||||
FromSec = NowSec - ?DEFAULT_SEARCH_DAYS * 86400,
|
||||
ToSec = NowSec + ?DEFAULT_SEARCH_DAYS * 86400,
|
||||
{calendar:gregorian_seconds_to_datetime(FromSec),
|
||||
calendar:gregorian_seconds_to_datetime(ToSec)};
|
||||
ensure_time_range(From, undefined) ->
|
||||
{From, calendar:gregorian_seconds_to_datetime(
|
||||
calendar:datetime_to_gregorian_seconds(From) + ?DEFAULT_SEARCH_DAYS * 2 * 86400)};
|
||||
ensure_time_range(undefined, To) ->
|
||||
{calendar:gregorian_seconds_to_datetime(
|
||||
calendar:datetime_to_gregorian_seconds(To) - ?DEFAULT_SEARCH_DAYS * 2 * 86400), To};
|
||||
ensure_time_range(From, To) -> {From, To}.
|
||||
|
||||
sort_events(Events, SortField, Order) ->
|
||||
Field = binary_to_existing_atom(SortField, utf8),
|
||||
Sorted = lists:sort(
|
||||
fun(A, B) ->
|
||||
ValA = event_field(A, Field),
|
||||
ValB = event_field(B, Field),
|
||||
if Order == <<"asc">> -> ValA =< ValB;
|
||||
true -> ValA >= ValB
|
||||
end
|
||||
end, Events),
|
||||
Sorted.
|
||||
|
||||
event_field(Event, created_at) -> Event#event.created_at;
|
||||
event_field(Event, start_time) -> Event#event.start_time;
|
||||
event_field(Event, title) -> Event#event.title;
|
||||
event_field(Event, status) -> Event#event.status;
|
||||
event_field(_, _) -> undefined.
|
||||
|
||||
%% Валидация времени события (без учёта пользователя)
|
||||
validate_event_time(StartTime) ->
|
||||
validate_event_time(StartTime, undefined).
|
||||
@@ -214,6 +323,7 @@ validate_update({location, Value}, _) ->
|
||||
validate_update({tags, Value}, _) when is_list(Value) -> true;
|
||||
validate_update({capacity, Value}, _) when is_integer(Value), Value > 0 -> true;
|
||||
validate_update({online_link, Value}, _) when is_binary(Value) -> true;
|
||||
validate_update({status, Value}, _) when is_atom(Value) -> true;
|
||||
validate_update(_, _) -> false.
|
||||
|
||||
get_exceptions(MasterId) ->
|
||||
|
||||
Reference in New Issue
Block a user