RPU v2, promo/bank система, админка, оптимизации

— RPU v2: бюджет удачи, потолок (ceiling), комбек после
  проигрышей, hot/cold анализ, интеграция с promo-фазами
— Bank API: mock-банк для карты (депозит/вывод с балансом 1000₽)
— Promo-сервис: автогенерация промокодов каждый час, типы
  (card_to_site, luck_boost, ceiling_boost, free_case, reset_streak)
— Promo-фазы: LUCKY/NEUTRAL/UNLUCKY, переключение каждые 1-6ч
— Admin: история транзакций (user_history), управление промокодами,
  управление promo-фазами, API фильтров/коллекций, RPU info
— Database: WAL mode, новые поля RPU v2, TransactionLog,
  карточный баланс, статистики (streaks, ceiling, budget)
— Frontend: /deposit, /promo/activate, /withdraw, /card-balance,
  /online-count, /rpu-info, lifespan вместо startup event
— UI: user_history.html, улучшения профиля, кейсов, навигации
— Cases: обновление cases.json (1892 строки новых данных)
— Achievements: кэш коллекций, оптимизация is_custom проверок
— Sounds: дополнительные звуковые эффекты
— Upgrade: интеграция с RPU (множители, streaks)
— .gitignore: исключены .db, .bak, __pycache__
This commit is contained in:
2026-07-25 13:11:08 +00:00
parent 6638ab99f3
commit 47c7f17c90
30 changed files with 5862 additions and 1295 deletions
+27
View File
@@ -4,6 +4,13 @@
{% block page_title %}📊 Дашборд{% endblock %}
{% block content %}
<div class="a-card" style="margin-bottom:1rem;padding:0.75rem">
<div style="display:flex;justify-content:space-between;align-items:center">
<strong>🎭 Промо-фаза:</strong>
<span id="promoPhaseInfo">Загрузка...</span>
</div>
</div>
<div class="a-stats">
<div class="a-stat">
<div class="as-label">Пользователей</div>
@@ -55,3 +62,23 @@
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
async function loadPromoPhase() {
try {
const r = await fetch('/admin/api/promo-phase');
const d = await r.json();
const el = document.getElementById('promoPhaseInfo');
if (d.current_phase === 'LUCKY') {
el.innerHTML = '<span style="color:#22c55e">🍀 LUCKY</span> (осталось ' + Math.floor(d.remaining_seconds / 60) + ' мин)';
} else if (d.current_phase === 'UNLUCKY') {
el.innerHTML = '<span style="color:#ef4444">💀 UNLUCKY</span> (осталось ' + Math.floor(d.remaining_seconds / 60) + ' мин)';
} else {
el.innerHTML = '<span style="color:#a3a3a3">⚪ NEUTRAL</span> (осталось ' + Math.floor(d.remaining_seconds / 60) + ' мин)';
}
} catch(e) {}
}
document.addEventListener('DOMContentLoaded', loadPromoPhase);
</script>
{% endblock %}