Yomu
Java Backend

Bacaan dan Kuis

Backend bacaan-kuis Java - artikel, soal kuis, percobaan pengguna, dan sinkronisasi hasil kuis ke Rust

Modul bacaan-kuis menyediakan konten artikel dan soal kuis yang terkait dengan artikel tersebut. Backend Java menyimpan artikel, mengelola kuis, mencegah pengguna mengerjakan kuis yang sama lebih dari sekali, menghitung skor dari jawaban pengguna, lalu menyinkronkan hasilnya ke engine Rust untuk gamifikasi.

Implementasi utama berada di package bacaankuis pada yomu-backend-java/src/main/java/id/ac/ui/cs/advprog/yomubackendjava/bacaankuis. Controller legacy BacaanKuisController sudah deprecated; endpoint aktif memakai ArticleController, QuizController, AdminArticleController, dan AdminQuizController.

Ringkasan Arsitektur

Komponen utama:

KomponenTanggung jawab
ArticleControllerMengekspos daftar dan detail bacaan untuk pengguna
QuizControllerMengambil soal kuis dan menerima submit jawaban pengguna
AdminArticleControllerMembuat dan menghapus artikel
AdminQuizControllerMembuat, memperbarui, dan menghapus soal kuis
InternalArticleControllerValidasi artikel untuk komunikasi internal dengan API key
ArticleServiceQuery artikel, filter kategori, validasi create, dan delete artikel beserta kuisnya
QuizServiceCek attempt, hitung skor, simpan attempt, dan sync hasil kuis
QuizManagementServiceValidasi dan operasi CRUD untuk soal kuis
QuizSyncClientKontrak sinkronisasi hasil kuis ke Rust
OutboxServiceMencatat kegagalan sinkronisasi quiz agar dapat di-retry

Endpoint

Public Article

MethodPathAuthKeterangan
GET/api/v1/articlesPublicAmbil semua bacaan
GET/api/v1/articles?category={category}PublicAmbil bacaan berdasarkan kategori, case-insensitive
GET/api/v1/articles/{id}PublicAmbil detail bacaan

Quiz Pengguna

MethodPathAuthKeterangan
GET/api/v1/quizzes/{article_id}JWTAmbil soal kuis untuk artikel tertentu
POST/api/v1/quizzes/{article_id}/submitJWTKirim jawaban dan hitung skor

Admin

MethodPathAuthKeterangan
POST/api/v1/admin/articlesAdminBuat artikel
DELETE/api/v1/admin/articles/{id}AdminHapus artikel dan kuis terkait
POST/api/v1/admin/articles/{article_id}/quizzesAdminBuat soal kuis untuk artikel
PATCH/api/v1/admin/quizzes/{id}AdminPerbarui soal kuis
DELETE/api/v1/admin/quizzes/{id}AdminHapus soal kuis

Internal

MethodPathAuthKeterangan
GET/api/internal/articles/{article_id}/existsx-api-keyValidasi bahwa artikel ada di Core DB

Semua response mengikuti wrapper umum:

{
  "success": true,
  "message": "Daftar bacaan",
  "data": {}
}

Endpoint kuis mengambil user dari CurrentUser, sehingga user tidak bisa mengirim user_id sendiri dari request body. Ini mencegah spoofing identitas saat submit kuis.

Model Data

Article

Article disimpan di tabel articles.

FieldTypeKeterangan
idStringPrimary key artikel
titleStringJudul bacaan
contentTEXTIsi bacaan
categoryStringKategori bacaan, dipakai untuk filter daftar

Quiz

Quiz disimpan di tabel quizzes.

FieldTypeKeterangan
idStringPrimary key soal
article_idStringID artikel pemilik kuis
questionStringTeks pertanyaan
optionsStringPilihan jawaban, disimpan sebagai string
answerStringJawaban benar

Response soal kuis tidak mengembalikan field answer, sehingga jawaban benar tidak bocor ke client:

{
  "id": "quiz-1",
  "articleId": "article-123",
  "question": "Apa gagasan utama bacaan?",
  "options": "A. ...; B. ...; C. ...; D. ..."
}

UserAttempt

UserAttempt disimpan di tabel user_attempts.

FieldTypeKeterangan
idLongPrimary key auto-increment
user_idUUIDUser yang mengerjakan kuis
kuis_idStringSaat ini berisi article_id
completed_atLocalDateTimeWaktu kuis diselesaikan

Constraint unik user_id + kuis_id memastikan satu user hanya dapat menyelesaikan kuis untuk satu artikel satu kali.

Alur Submit Kuis

Request submit:

{
  "answers": [
    {
      "quiz_id": "quiz-1",
      "answer": "A"
    },
    {
      "quiz_id": "quiz-2",
      "answer": "C"
    }
  ]
}

Alur eksekusi:

Response submit:

{
  "score": 50.0,
  "accuracy": 50.0,
  "correct_count": 1,
  "total_questions": 2
}

score dan accuracy dihitung dari jumlah jawaban benar dibagi total soal, lalu dikali 100. Perbandingan jawaban memakai trim() dan equalsIgnoreCase().

Validasi

OperasiRule
Create artikelid, title, content, dan category wajib diisi
Delete artikelArtikel harus ada; kuis terkait dihapus lebih dulu
Create kuisarticle_id, id, question, options, dan answer wajib diisi
Update kuisKuis harus ada; question, options, dan answer wajib diisi
Get kuisUser harus login dan belum pernah menyelesaikan kuis artikel tersebut
Submit kuisUser harus login, article_id valid, answers tidak kosong
Jawaban submitquiz_id dan answer wajib diisi, quiz_id tidak boleh duplikat
Submit ulangDitolak dengan ConflictException

Jika Rust sedang tidak tersedia, submit kuis tetap berhasil setelah attempt tersimpan. Kegagalan sinkronisasi dicatat sebagai event QUIZ_SYNC di outbox untuk retry.

Integrasi Modul Lain

Modul forum memakai ArticleService.findById(articleId) untuk memastikan komentar hanya dibuat pada artikel yang benar-benar ada.

Modul gamifikasi Rust menerima hasil kuis melalui QuizSyncClient dengan payload:

{
  "user_id": "3b9d1a20-37d2-4dd3-bf0e-0e04d36f16ef",
  "article_id": "article-123",
  "score": 100.0,
  "accuracy": 100.0
}

Endpoint internal /api/internal/articles/{article_id}/exists memakai header x-api-key dan perbandingan konstan melalui MessageDigest.isEqual. Endpoint ini mengembalikan status artikel untuk service internal yang perlu memvalidasi referensi artikel.

Testing

Backend Java memiliki test khusus bacaan-kuis:

TestFokus
ArticleControllerTestKontrak endpoint daftar dan detail artikel
AdminArticleControllerTestCreate dan delete artikel oleh admin
AdminQuizControllerTestCreate, update, dan delete kuis
PublicQuizControllerTestAuth pada get/submit kuis dan penggunaan user dari JWT
InternalArticleControllerTestValidasi artikel internal dengan API key
QuizServiceTestSubmit, perhitungan skor, idempotensi attempt, dan fallback outbox
UserAttemptRepositoryTestConstraint unik attempt per user dan artikel
AuthForumBacaanKuisIntegrationTestIntegrasi auth, forum, dan bacaan-kuis

On this page