feat(matching): implement 3-tier account system with fairness-based recording assignment
Add account tier system (BASIC/SUPPORTER/COMFORT) to reduce recording burden for premium users while maintaining fairness through karma-based assignment. Database Changes: - Add AccountTier enum (BASIC, SUPPORTER, COMFORT) - Add User.accountTier with BASIC default - Add User.recordingsDone and User.recordingsReceived for karma tracking - Add EventParticipant.accountTierOverride for event-specific tier upgrades - Migration: 20251129220604_add_account_tiers_and_recording_stats Matching Algorithm Updates: - Implement fairness debt calculation: receivedCount - doneCount - Apply tier penalties: SUPPORTER (-10), COMFORT (-50) - New sorting priority: Location > Fairness > Load balancing - Add getEffectiveTier() helper for tier resolution with override support - Add getRecordingStatsForUsers() for fetching karma statistics Tier Behavior: - BASIC: Normal recording frequency (baseline, no penalty) - SUPPORTER: Moderately reduced frequency (fairness penalty -10) - COMFORT: Significantly reduced frequency (fairness penalty -50) - All tiers can still be assigned when no better candidates available Constants: - ACCOUNT_TIER enum in src/constants/tiers.js - FAIRNESS_SUPPORTER_PENALTY = 10 - FAIRNESS_COMFORT_PENALTY = 50 Tests: - Update tests for dual buffer system semantics - All 30 tests passing - Fix imports: HEAT_BUFFER → HEAT_BUFFER_BEFORE
This commit is contained in:
@@ -11,6 +11,13 @@ datasource db {
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
// Account tier enum
|
||||
enum AccountTier {
|
||||
BASIC
|
||||
SUPPORTER
|
||||
COMFORT
|
||||
}
|
||||
|
||||
// Users table
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
@@ -47,9 +54,15 @@ model User {
|
||||
failedLoginAttempts Int @default(0) @map("failed_login_attempts")
|
||||
lockedUntil DateTime? @map("locked_until")
|
||||
|
||||
avatar String? @db.VarChar(255)
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
avatar String? @db.VarChar(255)
|
||||
|
||||
// Account Tier & Recording Stats
|
||||
accountTier AccountTier @default(BASIC) @map("account_tier")
|
||||
recordingsDone Int @default(0) @map("recordings_done") // How many times this user recorded others
|
||||
recordingsReceived Int @default(0) @map("recordings_received") // How many times others recorded this user
|
||||
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
// Relations
|
||||
messages Message[]
|
||||
@@ -189,12 +202,13 @@ model Rating {
|
||||
|
||||
// Event participants (tracks which users joined which events)
|
||||
model EventParticipant {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int @map("user_id")
|
||||
eventId Int @map("event_id")
|
||||
competitorNumber Int? @map("competitor_number") // Bib number - one per user per event
|
||||
recorderOptOut Boolean @default(false) @map("recorder_opt_out") // Opt-out from being a recorder
|
||||
joinedAt DateTime @default(now()) @map("joined_at")
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int @map("user_id")
|
||||
eventId Int @map("event_id")
|
||||
competitorNumber Int? @map("competitor_number") // Bib number - one per user per event
|
||||
recorderOptOut Boolean @default(false) @map("recorder_opt_out") // Opt-out from being a recorder
|
||||
accountTierOverride AccountTier? @map("account_tier_override") // Override user's global tier for this event (e.g., Comfort Pass)
|
||||
joinedAt DateTime @default(now()) @map("joined_at")
|
||||
|
||||
// Relations
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
Reference in New Issue
Block a user