Popularity — счётчики установок¶

Popularity-секция админ-панели управляет рейтингами в каталоге. Сколько раз кто-то ставил redux, gunpack, armor pack, preset. Сортировка по популярности в UI — топ-моды наверху.
Структура¶
Популярность хранится на каждой таблице каталога как popularity int. Это денормализованный счётчик, а не отдельная таблица. Простота важнее нормализации тут.
-- В каждой каталог-таблице есть колонка
alter table redux_items add column popularity int default 0;
alter table gunpacks add column popularity int default 0;
alter table armor_packs add column popularity int default 0;
-- ...
Increment-flow¶
При каждой успешной установке мод увеличиваем счётчик. Это делается на сервере (Supabase RPC) чтобы юзер не мог накрутить ручными запросами:
public async Task<InjectResultDto> ReduxInstallAsync(string reduxId, string? versionId)
{
var result = await _injector.InstallAsync(reduxId, versionId);
if (result.Success)
{
// fire-and-forget
_ = _supa.RpcAsync("increment_redux_popularity", new { redux_id = reduxId });
}
return result;
}
-- Supabase RPC
create or replace function increment_redux_popularity(redux_id uuid)
returns void as $$
begin
update redux_items set popularity = popularity + 1 where id = redux_id;
end;
$$ language plpgsql security definer;
security definer — функция выполняется с правами owner'а, юзеру даже не нужны row-level UPDATE permissions. Это даёт нам контроль: юзер не может сделать update redux_items set popularity = 9999, только вызвать += 1 через RPC.
Decay¶
Без decay'а старые моды (которые ставили миллион раз 5 лет назад) навсегда доминируют в топе. Это плохо для discovery новых.
Раз в день Supabase scheduled job делает:
-- Каждый день в 03:00 UTC
update redux_items set popularity = floor(popularity * 0.99);
update gunpacks set popularity = floor(popularity * 0.99);
-- ...
* 0.99 = 1% decay в день. За год — 0.99^365 ≈ 0.026, то есть мод теряет ~97% веса за год если новых установок нет.
В сочетании с свежими +1 это даёт balanced ranking: новый популярный мод быстро поднимается, старый-классный держится если активно ставится, забытый постепенно скатывается.
Управление вручную¶
В админке есть manual override — adminCatalogUpdate на колонку popularity. Используется редко:
- При импорте старых архивных данных (раньше в БД не было popularity, проставили ручную «прикинутую» цифру).
- При раскрутке нового мода — админ ставит boost
+1000чтобы новый redux попал в топ при release'е. - При очистке — занулить popularity у мода который был накручен ботами.
Popular choices таблица¶
Дополнительная таблица popular_choices — это отдельный курируемый список «что популярно сейчас». Не autoupdate'ится, заполняется вручную админом:
create table popular_choices (
id uuid primary key,
kind text not null, -- 'redux' | 'gunpack' | 'armor' | 'builds'
target_id uuid not null, -- FK на соответствующую таблицу
display_order int,
badge_text text, -- "TRENDING", "NEW", "STAFF PICK"
added_at timestamptz default now()
);
Это позволяет вручную пушить «эту неделю смотрим Hunter Reborn 4.0» с badge'м TRENDING. UI рендерит эту секцию отдельной полосой на главной.
Что админ видит в UI¶
Таблица всех каталог-айтемов с колонкой popularity. Можно:
- Sort by popularity (descending) — увидеть текущий топ.
- Manual
+100/-50через input. - Reset to zero (например после очистки бот-накруток).
- Toggle freeze — если флаг
popularity_frozen = trueна row, decay-job игнорирует. Используется для исторических моды-эталонов которые не хотим терять.
Где popularity влияет¶
- Сортировка в каталоге (
Browse → Reduxпо умолчанию). - Top-N виджеты на главной странице.
- Recommendation algorithm в HntCode апплае (если в коде ссылка на удалённый мод — предлагаем популярную замену).