feat(enakgame): weekly leaderboard

GET /customer/enakgame/leaderboard ranks the customers of an organization by
the EnakCoin they won from games in one week, Monday to Sunday in
Asia/Jakarta. Only completed, unflagged sessions count, by the week they
started; entry cost is not subtracted. Equal totals rank by who reached
theirs first, so no two customers share a rank.

Optional date (any day of the week, this week by default), game_id and limit
(10, at most 100). The response holds the top ranks and, under me, the
customer asking wherever they rank. Other customers are shown by a masked
name: the first word whole, the others by their first letter; a word with a
digit keeps only its first character.

The EnakGame web game shows it: integration-enakgame.md §10 (checklist now
§11) and RFC §11. The DB test needs TEST_DATABASE_URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
efrilm
2026-10-12 16:15:02 +07:00
co-authored by Claude Opus 5.5
parent c43baa53e1
commit 3af16a1fbc
10 changed files with 398 additions and 4 deletions
+47 -3
View File
@@ -1,6 +1,6 @@
# Integrasi EnakGame: Game Client (Phaser)
**Untuk:** tim game EnakGame (client Phaser) · **Base URL:** `/api/v1` · **Per:** 9 Okt 2026
**Untuk:** tim game EnakGame (client Phaser) · **Base URL:** `/api/v1` · **Per:** 12 Okt 2026
Kamu mengerjakan **game EnakGame**: game web (Phaser) yang dibuka aplikasi customer di
dalam webview dari `game_url` sebuah game. Game inilah yang menjalankan satu kali main
@@ -13,7 +13,7 @@ Pembagian tugas dengan aplikasi customer:
| Aplikasi customer ([`integration-mobile-customer.md`](./integration-mobile-customer.md)) | Game EnakGame (dokumen ini) |
|---|---|
| Login customer, menyimpan token | Menerima token dari aplikasi lewat bridge (§4); bila tidak ada, meminta customer login (§5) |
| Daftar game, membuka `game_url` di webview | Start session, main, complete, tampilkan hadiah |
| Daftar game, membuka `game_url` di webview | Start session, main, complete, tampilkan hadiah, leaderboard mingguan (§10) |
| Saldo, riwayat, voucher, PIN | Memberi tahu aplikasi saat saldo berubah atau game ditutup |
Alasan di balik aturannya ada di [`rfc-enakgame.md`](./rfc-enakgame.md) dan
@@ -106,6 +106,7 @@ memakai header `Authorization: Bearer <token>`. Request ber-body memakai
| 3 | `POST /customer/enakgame/sessions/:id/complete` | – | `{ "score"?, "outcome"?, "data"? }` | Permainan selesai | §6.3 |
| 4 | `GET /customer/enakgame/sessions/:id` | – | – | Pemulihan setelah reload, bila `session_id` tersimpan | §6.4 |
| 5 | `GET /customer/enakgame/sessions?game_id=&status=&page=&limit=` | – | – | Pemulihan setelah reload, bila `session_id` tidak tersimpan | §6.4 |
| 6 | `GET /customer/enakgame/leaderboard?date=&game_id=&limit=` | – | – | Layar leaderboard; muat ulang setelah complete | §10 |
Game tidak memanggil endpoint lain. Registrasi, saldo, riwayat, voucher, dan PIN
adalah tugas aplikasi customer.
@@ -601,7 +602,49 @@ saja.
---
## 10. Checklist
## 10. Leaderboard mingguan — `GET /customer/enakgame/leaderboard`
Peringkat customer satu organisasi menurut EnakCoin yang dimenangkan dari game dalam
satu minggu, Senin 00:00 sampai Minggu 23:59 WIB. Yang dihitung hanya session
`COMPLETED`, menurut minggu saat session dimulai; entry cost tidak mengurangi. Total
yang sama diurutkan menurut siapa yang mencapainya lebih dulu, jadi tidak ada peringkat
kembar.
| Query | Default | Keterangan |
|---|---|---|
| `date` | Minggu ini | Hari mana pun dalam minggu yang diminta, `YYYY-MM-DD`. Untuk minggu lalu, kirim tanggal 7 hari sebelum `week_start` minggu ini |
| `game_id` | Semua game | Leaderboard satu game, mis. `game_id` dari `init` untuk "peringkat game ini" |
| `limit` | 10 | Jumlah peringkat teratas, maks 100 |
```json
{
"week_start": "2026-10-12",
"week_end": "2026-10-18",
"game_id": null,
"entries": [
{ "rank": 1, "name": "Budi S******", "coins": 120, "is_me": false },
{ "rank": 2, "name": "Siti N** A*****", "coins": 90, "is_me": true }
],
"me": { "rank": 2, "name": "Siti N** A*****", "coins": 90, "is_me": true }
}
```
- Nama customer lain sudah disamarkan server: kata pertama utuh, kata berikutnya hanya
huruf pertama. Tampilkan apa adanya; untuk baris `is_me: true` tampilkan "Kamu".
- `me` adalah posisi customer yang login, juga bila di luar `limit`: tampilkan sebagai
baris tersendiri di bawah daftar. `null` bila belum menang EnakCoin minggu itu
("Main untuk masuk leaderboard").
- `entries` kosong (`[]`, tidak pernah `null`) bila belum ada yang menang minggu itu.
- Tampilkan rentang minggu dari `week_start`–`week_end`; jangan menghitung minggu
sendiri dari jam perangkat.
- Leaderboard tidak berubah sendiri. Muat ulang setelah complete berhasil, atau saat
layar leaderboard dibuka.
- `date` yang bukan `YYYY-MM-DD` atau `game_id` yang bukan UUID ditolak `304` (bug di
game; pesan umum).
---
## 11. Checklist
- [ ] Game baru sudah didaftarkan bersama tim backoffice (§2.3); `slug` diperiksa saat `init` (§6.1).
- [ ] Body complete sesuai jenis hadiah game (§2.2).
@@ -616,5 +659,6 @@ saja.
- [ ] Complete hanya mengirim `score` / `outcome` / `data`, tidak pernah hadiah.
- [ ] Hadiah di layar dari `reward_total`; `limited_by` dan `REFUNDED` ditangani.
- [ ] Spin berhenti di `prize.entry`.
- [ ] Leaderboard (§10): rentang minggu dari `week_start`/`week_end`, baris `is_me` sebagai "Kamu", `me` di luar `limit` tampil di bawah daftar, dimuat ulang setelah complete.
- [ ] Complete diulang dengan aman saat gagal; timeout `expires_at` ditangani.
- [ ] `balance_changed` dikirim setelah start dan complete; `close` saat keluar.
+3 -1
View File
@@ -843,6 +843,7 @@ tambahkan snapshot harian, bukan cache yang di-invalidate.
| `POST` | `/sessions/:id/complete` | §7.2. Idempotent tanpa header |
| `GET` | `/sessions/:id` | Status dan hasil |
| `GET` | `/sessions` | Riwayat main; filter opsional `game_id`, `status` (dipakai game untuk menemukan session `STARTED` setelah reload) |
| `GET` | `/leaderboard` | Peringkat mingguan (Senin–Minggu, Asia/Jakarta) org customer menurut EnakCoin yang dimenangkan dari game. Query opsional `date` (hari mana pun dalam minggu itu), `game_id`, `limit` (default 10, maks 100) |
Prefix `/enakgame` dipakai karena `/customer/games` sudah dipakai alur spin lama.
@@ -940,7 +941,8 @@ Ditemukan saat memetakan code. Tidak memblokir RFC ini, tapi sebagian berdampak
## 16. Di Luar Scope
- **Mission** (PRD §3.1, §19). PRD belum mendefinisikan aturannya.
- **Leaderboard** (PRD §15).
- **Leaderboard event** (PRD §15). Leaderboard mingguan umum sudah ada (§11, ditambahkan
2026-10-12); leaderboard per event dengan aturannya sendiri belum.
- **Reward `TIERED`** (§8).
- **Budget Controller automatic mode** (D9).
- **Hosting dan build Phaser.** RFC ini hanya mendefinisikan API yang dipanggil game.