Перейти к содержанию

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

Подборка на главной — три закреплённых редакса (Allegri V3, Zombie, Gucci V2)

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) чтобы юзер не мог накрутить ручными запросами:

HunterGraphics.Shell/Bridge/AppBridge.cs
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 overrideadminCatalogUpdate на колонку popularity. Используется редко:

  • При импорте старых архивных данных (раньше в БД не было popularity, проставили ручную «прикинутую» цифру).
  • При раскрутке нового мода — админ ставит boost +1000 чтобы новый redux попал в топ при release'е.
  • При очистке — занулить popularity у мода который был накручен ботами.

Дополнительная таблица 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 апплае (если в коде ссылка на удалённый мод — предлагаем популярную замену).

Это конец admin-секции. Дальше: HNT-коды →