概要
ランキングへの投票機能。シングル投票(1選択肢)と TOP3投票(3選択肢の順位付き)の2モード。
クイズゲート機能で「分かってる人だけ投票できる」品質保証。再投票は最大3回までポイント消費で許可。
関連画面
処理フロー(シングル投票・quiz無)
sequenceDiagram
participant U as User
participant V as VoteView
participant VM as RankingDetailViewModel
participant VS as VoteService
participant PS as PointService
participant DB as Supabase
U->>V: 選択肢タップ + 投票
V->>VM: castVote()
VM->>PS: consumePoints (revote時のみ)
PS-->>VM: ok
VM->>VS: castVote()
VS->>DB: votes INSERT
alt Unique制約違反
DB-->>VS: 23505エラー
VS-->>VM: VoteError.alreadyVoted
else 成功
DB-->>VS: ok
VS->>DB: choices.voteCount RPC
VS->>DB: rankings.voteCount RPC
DB-->>VS: ok
end
VS-->>VM: 完了
VM->>V: hasVotedOptimistically=true
V->>U: ResultView へ遷移
投票モード
| モード | 動作 | 消費 |
RankingMode.single | 1選択肢を選ぶ | 初回0pt / revote1: 3pt / revote2: 7pt |
RankingMode.threePoint | 3枠を順位付きで選ぶ | 同上(3件のVote INSERT) |
ビジネスルール
- 投票は1ユーザー1ランキング1回が原則(Unique制約: rankingId+userId)
- 再投票は最大3回(voteNumber: 1, 2, 3)まで、ポイント消費で許可
- クイズ付きランキングは全問正解後のみ投票可能
- 期間外の投票は受け付けない(accessPolicy で判定)
- 投票直後は
hasVotedOptimistically で UI 即時更新(楽観UI)
- 削除済みランキング(status: deleted)への投票は不可
クイズゲート機能
- ランキング作成者が3問のクイズを設定
- 全問正解しないと投票画面に進めない
- 不正解時は最初からやり直し(部分通過なし)
- クイズ作成は
+15pt の追加コスト
外部連携
| 連携先 | 用途 |
| Supabase votes表 | 投票記録 |
| Supabase RPC | choices.voteCount / rankings.voteCount の更新 |
| PointService | 再投票時のポイント消費 |
エラー処理
| 発生条件 | 対応 |
| ポイント不足 | 「ポイントが足りません」アラート |
| 既に投票済み(Unique違反) | 「すでに投票済みです」 + ResultView へ |
| 期間外 | 「投票期間外です」 |
| 非アトミック途中失敗 | cancelRefund で補償処理 |
実装メモ
- VoteService.castVote() の4ステップ非アトミック実装
- isUniqueViolation() で既投票検出(PostgreSQL 23505 SQLState)
- RankingDetailViewModel.nextVoteNumber で再投票回数管理
- Supabase Edge Function 化(v1.1検討)でアトミック化
変更履歴
| バージョン | 日付 | 変更内容 |
| 1.0 | 2026-05-09 | 初版作成 |