Skip to main content

Daily Spins

1. Summary

Goal: Retention и engagement через систему рулеток. Бесплатный ежедневный спин — причина заходить каждый день. Платные спины — sink для внутренней валюты.

User Value: Получение наград (Scrap, XP, предметы для крафта) с гарантированным бесплатным ежедневным входом. Путь: Активность → Daily Spin → Награды → Крафт.


2. Business Logic

Types of Spins

Доступ: Бесплатный, один раз в сутки

Cooldown: Регулируется DailySpin.cooldownHours (по умолчанию 24ч)

Tracking: Per-spin cooldown через SpinResult.spunAt

Цель: Retention — причина заходить каждый день

Spinning Mechanics

Алгоритм спина (реализован в UserSpinService):

1. Validation

  • Спин должен быть активен (isActive: true)
  • Проверка периода доступности (для Special)
  • Проверка per-spin cooldown

2. Payment Strategy

Порядок проверки
  1. Если priceScrap = 0 и pricePoints = null → бесплатный спин
  2. Если currencyType = SCRAP → проверка user.scrap >= priceScrap
  3. Если currencyType = STREAK_POINTS → проверка user.streakPoints >= pricePoints
  4. Всё в одной atomic transaction

3. RNG & Boost

Weighted Random выборка: P(item) = weight / sum(weights)

Luck Pool увеличивает шансы FRAGMENT/BLUEPRINT для активных игроков (× 3-13).

4. Reward Distribution

  • SCRAP: Начисляется на баланс + применяется SCRAP_BUFF если активен
  • XP: Начисляется + применяется XP_BUFF если активен
  • ITEM: Добавляется в инвентарь с sourceType: DAILY_SPIN

5. Side Effects

  • Запись в SpinResult с snapshot награды
  • Публикация в Live Feed (если награда значительная)
  • Passive Income для рефереров (если Scrap)
  • Обновление Season Stats

Edge Cases

Что видит пользователь (UI):

СитуацияUI поведение
❌ Баланс < ценыКнопка disabled, показ недостающей суммы
⏱️ Cooldown активенТаймер обратного отсчёта
📅 Вне периодаСпин скрыт или показана дата начала
🎰 Несколько спиновКарусель/список активных спинов
Backend Error Codes (для API/тестов)
КодHTTPСообщение
SPIN_NOT_FOUND404"Рулетка не найдена"
SPIN_NOT_AVAILABLE400"Рулетка недоступна в данный период"
INSUFFICIENT_BALANCE400"Недостаточно Scrap"
INSUFFICIENT_STREAK_POINTS400"Недостаточно Streak Points"
COOLDOWN_ACTIVE400"Спин на кулдауне. Попробуйте через X минут"

3. ADR (Architectural Decisions)

Почему cooldownHours на DailySpin, а не SpinType?

Проблема: Несколько спинов могут иметь разные кулдауны. Daily = 24ч, Paid = 0ч.

Решение: Кулдаун управляется на уровне DailySpin.cooldownHours, не SpinType.

Альтернативы (отклонены):

  • SpinType.cooldownHours — не поддерживает разные кулдауны для спинов одного типа
  • Глобальный User.lastDailySpin — не поддерживает multi-spin

Последствия: Гибкость настройки, но требует проверки кулдауна per-spin.

Почему Per-Spin Cooldown Tracking?

Критично для Multi-Spin

Пользователь может крутить разные рулетки параллельно. Нельзя блокировать все спины одним таймером.

Решение: Кулдаун проверяется через SpinResult.spunAt для конкретной пары (userId, spinId).

14:00 — пользователь крутит Рулетка #1
14:05 — пользователь крутит Рулетка #2 ✅ (разные спины)
14:25 — пользователь пытается крутить Рулетка #1 ❌ (кулдаун)

Почему RewardSnapshot в SpinResult?

Проблема: Награды могут быть удалены/изменены, но история должна показывать что выиграл пользователь.

Решение: JSON snapshot с полными данными награды на момент спина.

Формат RewardSnapshot
{
"id": "reward-id",
"type": "SCRAP",
"name": "500 Scrap",
"amount": 500,
"itemId": null,
"itemName": null,
"itemImageUrl": null,
"itemTier": null,
"buffBonus": {
"type": "SCRAP_BUFF",
"baseAmount": 500,
"bonusAmount": 150,
"multiplier": 1.3
}
}

4. Architecture

Services Overview

Key Components

КомпонентПутьОписание
UserSpinServicebackend/src/domains/cases/services/user-spin.service.tsОсновная логика спина
AdminSpinServicebackend/src/domains/cases/services/admin-spin.service.tsCRUD + статистика
DailySpinRepositorybackend/src/domains/cases/repositories/daily-spin.repository.tsData Access
User Routesbackend/src/domains/cases/routes/user-spin.routes.tsUser API
Admin Routesbackend/src/domains/cases/routes/admin-daily-spins.routes.tsAdmin API
Schemasbackend/src/domains/cases/schemas/user-spin.schemas.tsВалидация
Shared Domain

Код daily-spin находится в domains/cases вместе с кейсами — общая RNG механика.


5. Database Schema

Models

МодельОписаниеКлючевые поля
SpinTypeГруппировка спиновslug, isDailyFree, cooldownHours
DailySpinКонфигурация рулеткиspinTypeId, priceScrap, currencyType, cooldownHours
SpinItemСектор рулеткиspinId, rewardId, weight, displayDropChance
SpinResultИстория спиновuserId, spinId, rewardSnapshot, spunAt

Relationships


6. API Endpoints

МетодЭндпоинтОписаниеDocs
GET/api/daily-spin/listВсе активные спины (карусель)
GET/api/daily-spin/:spinId/check-cooldownПроверка кулдауна и баланса
POST/api/daily-spin/:spinId/spinВыполнить спин
GET/api/daily-spin/historyИстория спинов пользователя

  • Cases — похожая RNG механика
  • Buffs — SCRAP_BUFF и XP_BUFF влияют на награды
  • Streaks — источник Streak Points
  • Inventory — куда попадают выигранные предметы
  • Budget — контроль выдачи редких наград