Yomu
Rust Backend

Modul Gamification

Pelacakan pencapaian, misi harian, dan sistem poin hadiah

Modul Gamification menangani tonggak pencapaian (achievement milestones), misi harian, dan distribusi hadiah. Ia memproses penyelesaian kuis dan memperbarui progres pengguna secara near real-time.

Konteks Terbatas

Modul ini memiliki:

  • Definisi pencapaian dan pelacakan progresi
  • Pengaturan dan penyelesaian misi harian
  • Akumulasi dan klaim poin hadiah
  • Pembukaan pencapaian pada penyelesaian tonggak

Entitas

// Achievement: mendefinisikan hadiah untuk menyelesaikan tugas
pub struct Achievement {
    pub id: Uuid,
    pub title: String,
    pub description: String,
    pub type_: AchievementType,  // Common, Rare, Epic, Legendary
    pub points: i32,
    pub order: i32,             // Urutan tampilan
}

// Daily mission: direset setiap hari, melacak progres
pub struct DailyMission {
    pub id: Uuid,
    pub title: String,
    pub description: String,
    pub target_value: i32,      // misalnya, 5 kuis diselesaikan
    pub points: i32,
    pub reward_amount: i32,     // Mata uang dalam game
}

// User achievement: melacak progres pengguna
pub struct UserAchievement {
    pub user_id: Uuid,
    pub achievement_id: Uuid,
    pub progress: i32,
    pub completed_at: Option<DateTime<Utc>>,
    pub claimed: bool,
}

// User mission: pelacakan untuk misi harian
pub struct UserMission {
    pub user_id: Uuid,
    pub mission_id: Uuid,
    pub progress: i32,
    pub completed_at: Option<DateTime<Utc>>,
    pub claimed: bool,
}

Tipe Pencapaian

TipeKelangkaanBonus Poin
Common60%10
Rare25%25
Epic10%50
Legendary5%100

Trait Repositori

#[async_trait]
pub trait AchievementRepository: Send + Sync {
    async fn get_all(&self) -> Result<Vec<Achievement>, RepositoryError>;
    async fn get_by_id(&self, id: Uuid) -> Result<Option<Achievement>, RepositoryError>;
    async fn insert_user_achievement(
        &self,
        ua: &UserAchievement,
    ) -> Result<(), RepositoryError>;
    async fn update_user_achievement_progress(
        &self,
        user_id: Uuid,
        achievement_id: Uuid,
        delta: i32,
    ) -> Result<(), RepositoryError>;
}

#[async_trait]
pub trait MissionRepository: Send + Sync {
    async fn get_daily_missions(&self) -> Result<Vec<DailyMission>, RepositoryError>;
    async fn get_user_mission(
        &self,
        user_id: Uuid,
        mission_id: Uuid,
    ) -> Result<Option<UserMission>, RepositoryError>;
    async fn insert_user_mission(
        &self,
        um: &UserMission,
    ) -> Result<(), RepositoryError>;
    async fn claim_mission_reward(
        &self,
        user_id: Uuid,
        mission_id: Uuid,
    ) -> Result<i32, RepositoryError>;  // Mengembalikan poin yang diberikan
}

Use Case

SyncQuizGamificationUseCase

Memproses penyelesaian kuis: memeriksa pencapaian, memperbarui misi, memberikan poin. Idempoten per pasangan (user_id, article_id).

ClaimMissionRewardUseCase

Mengklaim hadiah misi harian. Memvalidasi penyelesaian misi, mendistribusikan hadiah, memperbarui saldo poin, mencegah klaim ganda.

Alur Eksekusi: Penyelesaian Kuis Memicu Pembaruan Gamifikasi

Endpoint

GET /api/v1/missions/daily

Mengambil misi harian yang aktif hari ini untuk pengguna yang terautentikasi.

Respons:

{
  "missions": [
    {
      "id": "uuid",
      "title": "Selesaikan 5 Kuis",
      "description": "Selesaikan 5 kuis hari ini",
      "target_value": 5,
      "current_value": 3,
      "completed": false,
      "claimed": false
    }
  ],
  "points_available": 100
}

POST /api/v1/missions/:id/claim

Mengklaim hadiah untuk misi yang telah diselesaikan.

Respons: 200 OK atau 400 (sudah diklaim, belum selesai) / 404

{
  "success": true,
  "message": "Hadiah diklaim!",
  "data": {
    "points_awarded": 25
  }
}

GET /api/v1/achievements/users/:user_id

Mengambil pencapaian pengguna dengan progres.

Respons:

{
  "achievements": [
    {
      "id": "uuid",
      "title": "Quiz Whiz",
      "description": "Selesaikan 100 kuis",
      "type": "Common",
      "progress": 75,
      "total": 100,
      "completed": false,
      "claimed": false,
      "points": 10
    }
  ],
  "total_points": 425
}

Validasi

FieldBatasan
scoreHarus ≥ 0
accuracyHarus antara 0.0 dan 100.0
target_valueHarus ≥ 1
progressTidak boleh melebihi target_value

Perhitungan Poin

Ketika SyncQuizGamificationUseCase berjalan:

// Pseudocode
poin_diperoleh = 0;

for pencapaian in daftar_pencapaian {
    if tonggak_tercapai(progres, pencapaian.target) {
        berikan_poin(pencapaian.points);
        poin_diperoleh += pencapaian.points;
    }
}

for misi in daftar_misi {
    if sudah_selesai(misi) && !misi.claimed {
        poin_diperoleh += misi.points;
    }
}

Strategi Pengujian

  • Unit: AchievementRepository, MissionRepository di-mock dengan mockall
  • Integrasi: Full stack dengan database uji, verifikasi pembukaan pencapaian
  • Kasus Tepi: Idempotensi, penyelesaian ganda, skor nol/tidak valid

On this page