Skip to main content

概要

NetTree のリーグ機能は、1週間ごとの XP をもとにしたリーグ制ランキングです。
  • すべてのユーザーは 1 週間単位の「リーグシーズン」に参加します
  • 同じティア・同じ週のユーザーは、最大 50 人のグループに分かれて競います
  • 週の終わりに 昇格 / 降格 / 残留 が判定され、次の週のティアとグループが決まります
  • ランキングは /dashboard/ranking から閲覧できます

ティア構成

リーグの階級は、次の 7 段階です。
  • Bronze(ブロンズ)
  • Silver(シルバー)
  • Gold(ゴールド)
  • Platinum(プラチナ)
  • Diamond(ダイアモンド)
  • Master(マスター)
  • Specialist(スペシャリスト)
アプリケーションコード上では、league_tier という enum として管理されています。

週次サイクル

週の単位

週は YYYY-Www(例: 2024-W05)という ISO 週番号形式の ID で管理されます。
  • 月曜 0:00 に週が始まり、日曜 23:50 までを 1 週間とみなします
  • 各週は week_cycle テーブルで管理され、isActive = true のレコードが「現在開催中の週」です

週の開始と終了

scripts/process-league-transition.ts によって、週次の切り替えとリーグ再編成が行われます。 ざっくりとした処理の流れは次の通りです。
  1. 現在アクティブな週(前週) を取得
  2. 現在日時から ターゲット週 ID(今週) を算出し、week_cycle を作成 or 更新
  3. 前週が別 ID の場合は、前週の isActivefalse に更新
  4. ターゲット週を isActive = true としてアクティブ化

ユーザー体験とルール

参加タイミング

  • ユーザーがレッスン完了などで XP を獲得したタイミングで、league-service.tsaddWeeklyXp が呼ばれます
  • その週のリーグに未参加の場合、自動的に Bronze ティアのグループに参加します
  • 参加後、同じ週・同じグループ内での weeklyXp が加算されていきます

ランキングとゾーン

各グループ内で、XP の多い順に並べてランキングを作成します。
  • ランキングは /ranking ページで 50 人まで表示されます
  • ダッシュボード(/dashboard)では、上位 10 人をカード形式で抜粋して表示します
  • UI 上では、順位に応じて以下のゾーンに色分けされます
    • 昇格ゾーン(Top 10): 次のティアへ昇格候補
    • 残留ゾーン(11〜30位): 現在のティアに残留
    • 降格ゾーン(31位以降): 1 ティア降格候補

昇格 / 降格ルール

週末のバッチ(scripts/process-league-transition.ts)で、前週の実績を元に昇格・降格が判定されます。
  • 各グループ内で XP 順に並べ、上位から以下を判定
    • 昇格枠: PROMOTION_SLOTS = 10 → 上位 10 人(人数が少ない場合はグループ人数まで)
    • 降格枠: DEMOTION_SLOTS = 10 → 下位 10 人(昇格者を除いた中から最大 10 人)
  • ティアの境界チェック
    • すでに最上位ティアの場合は昇格しません
    • すでに最下位ティアの場合は降格しません
  • 判定結果は league_participant に保存されます
    • finalRank: その週の最終順位
    • isPromoted: 昇格したかどうか
    • isDemoted: 降格したかどうか
次の週のティアは、これらのフラグにもとづいて決定されます。

次の週のグループ編成

新しい週のグループは、次のルールで編成されます。
  1. すべてのユーザーを取得
  2. 前週に参加していたユーザーは、昇格 / 降格結果に応じた 次のティア を決定
  3. 前週に参加していないユーザーは、デフォルトで Bronze ティア に配置
  4. ティアごとにユーザーをシャッフルし、最大 50 人ずつグループを作成
これにより、毎週ランダム性を持ちつつ、ティアとしての実力帯は維持されます。

データモデル

リーグ機能に関連する主なテーブルは次の通りです。

week_cycle

1 週間のリーグサイクルを表します。
  • id: 週 ID(2024-W05 など)
  • startDate, endDate: 週の開始・終了日時
  • isActive: 現在開催中かどうか

league_group

同じティア・同じ週の 最大 50 人の対戦グループ を表します。
  • id: グループ ID
  • weekCycleId: 紐づく週 (week_cycle.id)
  • tier: ティア(bronzespecialist

league_participant

各週・各グループにおける ユーザーの参加状況 を表します。
  • id: 参加レコード ID
  • userId: ユーザー ID
  • leagueGroupId: 参加しているグループ ID
  • weeklyXp: その週に獲得した XP
  • isPromoted: その週の結果として昇格したか
  • isDemoted: その週の結果として降格したか
  • finalRank: その週の最終順位
  • joinedAt: その週のリーグに参加した日時

関係性

  • week_cycle 1 件に対して、複数の league_group が紐づきます
  • league_group 1 件に対して、最大 50 件の league_participant が紐づきます
  • league_participantuser と 1 対 1 で結び付けられます(その週・そのグループにおける一意の参加情報)

処理フロー

学習時の XP 加算と参加処理

ユーザーがレッスンを完了すると、XP が加算されると同時にリーグ関連の処理が走ります。

週次リーグ遷移バッチ

scripts/process-league-transition.ts は、週替わりで以下の処理を行います。

画面との対応

/dashboard

ダッシュボードでは、リーグ情報は次のように利用されています。
  • 上部カードで「現在のリーグ」(例: ブロンズリーグ)と 週の残り時間 を表示
  • サイドのランキングカードで、上位 10 人の簡易リスト を表示
  • 自分の順位に応じて、バッジや色(昇格 / 降格ゾーン)を変化させてモチベーションを演出

/ranking

ランキングページでは、現在所属しているグループ内の詳細なランキングを表示します。
  • ヘッダー: 現在のティアと、ティア間の進行状況(Prev / Current / Next)を Duolingo 風に表示
  • 残り時間ウィジェット: 週の終了までのカウントダウン
  • テーブル形式のランキング:
    • Rank / 名前 / ステータス(昇格・降格・残留)/ XP
    • 自分の行はハイライト表示
  • グループに未参加の場合は、「ランキングに参加していません」とメッセージを表示し、学習開始を促す

関連ファイル

  • スキーマ
    • src/infrastructure/db/schema/league-schema.ts
  • サービス
    • src/services/league-service.ts
  • ミドルウェア / API
    • src/middlewares/get-league-ranking.ts
  • バッチ / スクリプト
    • scripts/process-league-transition.ts
    • scripts/update-weekly-xp.ts(デモ用の XP 更新スクリプト)
  • ルート / UI
    • src/routes/dashboard.tsx
    • src/routes/ranking.tsx