本アプリは SwiftDataではなく Supabase をバックエンドに採用。各モデルは struct + Codable で定義し、Supabase クライアント経由で読み書き。RLS(Row Level Security)でユーザー権限を制御。
ER 図(主要テーブル)
erDiagram
AppUser ||--o{ Ranking : creates
AppUser ||--o{ Vote : casts
AppUser ||--o{ PointTransaction : has
AppUser ||--o{ AppNotification : receives
AppUser ||--o{ Follow : "follows/followed_by"
Ranking ||--o{ Choice : contains
Ranking ||--o{ Vote : "voted on"
Ranking ||--o{ Quiz : has
Ranking ||--o{ ChoiceRequest : "requested for"
Ranking ||--o{ Report : reported
Choice ||--o{ Vote : "selected by"
AppUser {
UUID id
String displayName
Bool isPremium
Int points
Int loginStreak
Int freeCreationRemaining
}
Ranking {
UUID id
UUID creatorId
String title
String majorTag
String minorTag
String mode "single|threePoint"
String status "draft|active|ended|held|suspended|deleted"
Bool hasQuiz
Int voteCount
String voteTier "G..S"
Date startsAt
Date endsAt
Bool isPublic
Bool isAnonymous
}
Choice {
UUID id
UUID rankingId
String label
String shortLabel
String imageUrl
String imageStatus
Int displayOrder
Int voteCount
}
Vote {
UUID id
UUID rankingId
UUID userId
UUID choiceId
Int points
Int voteNumber
String ipHash
String deviceHash
}
Quiz {
UUID id
UUID rankingId
String question
String correctAnswer
String answerHint
Int displayOrder
}
主要モデル
AppUser
struct AppUser: Codable, Identifiable {
let id: UUID
var displayName: String
var isPremium: Bool
var points: Int
var loginStreak: Int
var freeCreationRemaining: Int
// ... 他プロフィール項目
}
Ranking
struct Ranking: Codable, Identifiable {
let id: UUID
let creatorId: UUID
var title: String
var majorTag: String
var minorTag: String?
var mode: RankingMode // single / threePoint
var status: RankingStatus // draft, active, ended, ...
var hasQuiz: Bool
var voteCount: Int
var voteTier: VoteTier // G〜S
var startsAt: Date
var endsAt: Date
var isPublic: Bool
var isAnonymous: Bool
var deletedAt: Date?
var pointsConsumed: Int
}
Choice / Vote / Quiz
struct Choice: Codable, Identifiable {
let id: UUID
let rankingId: UUID
var label: String
var shortLabel: String?
var imageUrl: String?
var imageStatus: String? // pending, approved 等
var displayOrder: Int
var voteCount: Int
}
struct Vote: Codable, Identifiable {
let id: UUID
let rankingId: UUID
let userId: UUID
let choiceId: UUID
let points: Int // 投票時の消費ポイント
let voteNumber: Int // 1=初投票, 2=revote1, 3=revote2
let ipHash: String?
let deviceHash: String?
}
struct Quiz: Codable, Identifiable {
let id: UUID
let rankingId: UUID
let question: String
let correctAnswer: String
let answerHint: String?
let displayOrder: Int
}
その他のモデル
ChoiceRequest — 選択肢追加申請(status: pending/approved/rejected, aiSuggestion含む)
AppNotification — ユーザー通知(type, message, isRead)
PointTransaction — ポイント取引履歴(action, amount)
Report — 通報(reason: discrimination/violence/sexual/spam等, isResolved)
Feedback — ユーザー意見(message, metadata)
Follow — フォロー関係(followerId, followedId)
TagDefinition — タグマスタ
主要 Enum
| Enum | ケース |
RankingStatus | draft / active / ended / held / suspended / deleted |
RankingMode | single(シングル) / threePoint(TOP3) |
VoteTier | G / F / E / D / C / B / A / S(投票数帯) |
PointAction | loginBonus / adReward / createRanking / revote1 / revote2 / viewResult / cancelRefund 他15種 |
ReportReason | discrimination / violence / selfHarm / sexual / illegal / personalInfo / spam / other |
ChoiceRequestStatus | pending / approved / rejected |
ポイント消費表
| アクション | 消費ポイント |
| ランキング作成(基本) | 20pt |
| クイズ付与 | +15pt |
| TOP3ランキング | 20pt |
| 選択肢拡張 +10 | 10pt |
| 選択肢拡張 +15 | 20pt |
| 期間延長 +7日 | 10pt |
| 期間延長 +14日 | 20pt |
| 1回目の再投票 | 3pt |
| 2回目の再投票 | 7pt |
| 結果閲覧(条件付) | 1〜3pt |
整合性に関する注意
非アトミック操作: RankingService.createRanking() と VoteService.castVote() は複数の Supabase RPC/INSERT をシーケンシャル実行する。途中失敗時の整合性を保証するため、本番運用では Edge Function 化を検討すること。
二重付与防止: PointService.claimLoginBonus() はクライアント側 lastLoginDate + DB point_transactions の二層チェックで日次ボーナスの二重付与を防いでいる。
関連ファイル
RankingStadium/Models/AppUser.swift
RankingStadium/Models/Ranking.swift
RankingStadium/Models/Choice.swift
RankingStadium/Models/Vote.swift
RankingStadium/Models/Quiz.swift
RankingStadium/Models/ChoiceRequest.swift
RankingStadium/Models/AppNotification.swift
RankingStadium/Models/PointTransaction.swift
RankingStadium/Models/Report.swift
RankingStadium/Models/Feedback.swift
RankingStadium/Models/TagDefinition.swift
RankingStadium/Services/SupabaseService.swift
変更履歴
| バージョン | 日付 | 変更内容 |
| 1.0 | 2026-05-09 | 初版作成(ソースコードからリバース) |