観戦参加仕様
イベントに観戦者として加わる経路と、その可否を決める条件をまとめる。
観戦者になる 3 つの経路
| 経路 | 実行者 | 「観戦を許可」の影響 | シナリオの観戦対応の影響 | 観戦料 |
|---|---|---|---|---|
| イベント詳細の「観戦する」 | 本人 | 受ける。許可していなければ押せない | 受ける | かかる場合がある |
| 観戦者の招待 | ホスト | 受けない | 受ける | 招待された本人が後から払う |
| UZU ADMIN のイベント作成での事前登録 | UZU ADMIN | 受けない | 受けない | 無料 (支払いが発生しない) |
「観戦を許可」が縛るのは本人による観戦参加だけで、観戦者を増やすこと自体を止める設定ではない。 観戦者を増やせるかどうかを決めるのは、下記のシナリオ側の観戦対応。
許可していないイベントは、シナリオの「通知登録」由来の立卓通知も送らない。「興味あり」由来の参加者募集通知は許可の有無にかかわらず送る。
シナリオの観戦対応
観戦ビューを持たないシナリオでは、ホストの設定に関係なく観戦を受け付けない。イベントが参照しているリビジョンで判定する。
| シナリオ | 観戦対応 |
|---|---|
| PlayScreen V2 | 常に対応 |
| PlayScreen V1 | 対応しない |
| UZU Code | manifest.json の spectator: true を宣言したときだけ対応。未宣言は対応しない |
- イベントが実際に観戦を受け付けるのは、観戦対応と「観戦を許可」の両方を満たすときだけ。GraphQL の
Event.allowSpectatorsはこの結果を返し、ホストの設定値そのものは返さない - 改稿が出ると、開始 6 時間以上前のイベントは新しいリビジョンへ差し替わる。観戦対応もそれに追従するため、立卓時ではなく参照のたびに判定する
- ホストの設定値は残る。後の改稿で観戦に対応すれば、設定がそのまま効く
- 観戦に対応しなくなっても、すでに観戦を予約した人は観戦者のまま残る。止まるのは新規の観戦参加と招待だけ
「観戦を許可」設定
イベントごとに持つ設定で、本人による観戦参加を受け付けるかどうかを決める。
立卓時
立卓画面の「観戦を許可する」トグルで決める。初期値は許可する。
- トグルが出るのは観戦に対応したシナリオだけ。それ以外のシナリオで立てたイベントは常に「許可しない」で作られる
- 自分がプレイ済みで、再プレイできるキャラ枠が無いシナリオではトグルを許可しない側へ倒せない (ホスト自身が観戦者として入るため)
作成後
ホストだけが、イベント詳細の観戦者欄にある「編集」から「観戦設定」を開いて切り替えられる。ホスト以外は変更できない。観戦に対応していないシナリオでは許可する側へ切り替えられない。
- 後から許可しない側へ倒しても、すでに観戦を予約した人は観戦者のまま残る。止まるのは新規の観戦参加だけ (「観戦設定」ダイアログにも同じ注記がある)
- 観戦者欄は「観戦を許可」が ON か、観戦者が 1 人以上いるときしか表示されない。 許可しない状態で観戦者が 0 人になると「観戦設定」を開く導線ごと消えるため、アプリからは許可する側へ戻せない
- UZU ADMIN のイベント作成は常に許可する状態で作る。UZU ADMIN に後から切り替える UI はない
観戦参加の条件
サーバーが弾く条件
上から順に評価し、1 つでも該当すればエラーになる。
- 利用停止中のユーザー
- 観戦者が上限に達している (通常 10 人 / ライブ配信イベント 1000 人)
- イベントが中止済み
- イベントが終了済み
- 「観戦を許可」が OFF
- シナリオが観戦に対応していない
- 同じイベントにプレイヤーとして参加している
- 参加者または観戦者とブロック関係にある (自分から / 相手からのどちらでも)
- 自分の別の予定に、観戦したい時間帯が完全に含まれている
- 観戦料が必要なのに、コイン残高もシナリオチケットも足りない
9 は完全に内包される場合だけで、部分的な重複は許可する。
アプリがボタンを無効にする条件
「観戦する」ボタンは次のいずれかに当てはまる間は押せない。上から順に評価し、最初に該当した理由が文言として表示される。
- イベントが終了済み
- イベントが中止済み
- 「観戦を許可」が OFF
- シナリオが観戦に対応していない
- プレイヤーとして参加している
- 観戦者が上限に達している
- シナリオのプレイにアプリの更新が必要
- フレンド限定イベントで、ホスト・プレイヤーにフレンドが 1 人もいない
- ホスト・プレイヤー・観戦者の誰かに自分がブロックされている
- ホスト・プレイヤーの誰かを自分がブロックしている
- 全体公開イベントで、利用制限中または電話番号未認証
2 つの判定がずれる箇所
同じ条件を見ているわけではないため、次の差がある。
| 条件 | ボタン | サーバー |
|---|---|---|
| アプリの更新が必要 | 無効 | 見ない |
| フレンド限定でフレンドがいない | 無効 | 見ない |
| 全体公開で利用制限中・電話番号未認証 | 無効 | 見ない |
| 自分がブロックしている観戦者がいる | 見ない | 弾く |
| 利用停止中のユーザー | 見ない | 弾く |
| 時間帯の二重予約 | 見ない | 弾く |
| コイン残高・チケットの不足 | 見ない | 弾く |
イベントに到達する経路
観戦するにはイベント詳細を開く必要がある。経路は 2 つ。
| 経路 | 見える条件 |
|---|---|
| 募集中イベント一覧 | リンク限定 (PRIVATE) は出ない。フレンド限定はホスト・プレイヤーにフレンドがいるときだけ出る。プレイヤーに自分がブロックされていると出ない |
共有 URL /events/<id> | イベント種別を問わず開ける。閲覧制限はない |
一覧の表示条件は「観戦を許可」を見ない。許可していないイベントも一覧と詳細には出て、「観戦する」ボタンだけが無効になる。
観戦料
- 未所有のシナリオセットを観戦するときだけ発生する。同一シナリオセットにプレイ済み・観戦済み・一人用購入済みのいずれかがあれば 0 円
- 同一シナリオセットの開始済みイベントで観戦画面に入っていれば 0 円。入った後の観戦キャンセルは返金しないため、精算済みの座席として扱う。開始後でもまだ入っていない予約は辞退で返金されるので、0 円の根拠にしない
- 支払いはコイン残高かシナリオチケット。チケットは有料のときだけ消費し、無料観戦では温存する
- ホストの招待では招待された本人が後から払う。未払いの間は入室せず、観戦履歴も残らない
観戦予約後の入室
観戦予約した時点で入室するのは次のいずれか。それ以外は開始を待つ。
- 突発イベント
- 参加者の誰かがすでにプレイ中
- イベントが開始済み
観戦の取り消し
返金の可否は「開始後に観戦画面へ一度でも入ったか」で決まる。
- 観戦画面に入る前の取り消しは、開始前・開始後を問わず観戦料を返金し、シナリオチケットを解放する
- 観戦画面に入った後の取り消しは返金しない。観戦者は取り消し済みとして記録が残り、同じイベントへ入り直すとその記録が復帰するので再課金されない
- 「観戦画面に入った」とみなすのは、開始済みイベントで観戦者がシナリオを視聴済みになった時点。次のいずれかで、その時刻を観戦者に記録する
- 開始済みイベントへの参加。参加と同時に観戦画面が開くので、参加直後に取り消しても返金されない
- 開始後に「入室」で観戦画面に入る
- 観戦画面 (待機中) に入ったままイベントが開始される
- 決済シートの返金条件の案内は、実際の入室の仕方に合わせて 3 通りに出し分ける
- 開始済み: 支払うとすぐに観戦画面が開き、支払い後に辞退しても返金されない
- 開始前で、支払うとすぐに待機中の観戦画面へ入る (突発イベント、または参加者の誰かがすでに入室している): その旨を添えたうえで、開始前の案内と同じ返金条件
- 開始前で、すぐには入室しない: 開始前の辞退と、開始後でも観戦画面に入る前の辞退は返金。開始時に観戦画面にいた場合や、開始後に入った後は返金しない
- 開始後に「観戦を辞退」を押すと、返金されない条件と、観戦画面に一度も入っていなければ開始後でも返金される旨を示す確認ダイアログを出す。開始前の辞退は常に返金されるので確認を出さない
- 奢りイベントのホストが観戦者として抜けた場合はイベント全体が中止になる。奢りでなければホスト権限を委譲し、委譲先がいなければ中止になる
実装の参照先
| 内容 | ファイル |
|---|---|
| 観戦許可フラグの定義 | backend/internal/ent/schema/event.go の allow_join_spectators |
| シナリオの観戦対応 | backend/internal/ent/0_scenario_revision.go の SupportsSpectators |
| イベントが観戦を受け付けるか | backend/internal/ent/0_event.go の AcceptsSpectators |
| 観戦許可フラグの切り替え | backend/internal/adapters/graph/event_change_allow_spectators.resolvers.go |
| 本人による観戦参加 | backend/internal/adapters/graph/spectator_join_event.resolvers.go |
| 「観戦する」ボタンの無効理由 | backend/internal/adapters/graph/event.resolvers.go の NonWatchableReason |
| ホストによる観戦者の招待 | backend/internal/adapters/graph/spectator_invite_spectators.resolvers.go |
| UZU ADMIN のイベント作成 (観戦者の事前登録) | backend/internal/adapters/graph/event_create_event_with_participant.resolvers.go |
| 募集中イベント一覧の表示条件 | backend/internal/usecase/event.go の IsVisibleRecruiting |
| 観戦料の計算 | backend/internal/usecase/event.go の CalculateSpectatorFee |
| ブロック判定の向き | backend/internal/usecase/user_relation.go |
| 観戦の取り消しと返金 | backend/internal/usecase/leave_spectator.go |
| 観戦画面に入った時刻の記録 | backend/internal/usecase/spectator.go の ChangeUserScenarioStatusWatched / ChangeAllSpectatorsStatusWatched |
| 立卓画面の観戦設定 | dart/apps/mobile/lib/views/pages/events/create/ui.dart の _SpectatorSettingCard |
| 観戦者欄と観戦設定ダイアログ | dart/apps/mobile/lib/views/pages/events/detail/event_data/maximized/index.dart, 同 spectator_setting.dart |
| 「観戦する」ボタン | dart/apps/mobile/lib/views/pages/events/detail/event_data/maximized/spectator_switch_button.dart |
| UZU ADMIN の作成モーダル | javascript/apps/admin/src/app/events/_components/create-event-modal.tsx |