Suistimal Raporları Modülü - Kod Analizi

admin/muzibu/abuse-reports • Hata & Eksiklik Raporu

6
Kritik Bug
4
Tasarım Hatası
5
Eksik Özellik

Basit Anlatım (Herkes İçin)

Suistimal Raporları modülü, müzik platformunda bir abonenin hesabını birden fazla kişiyle paylaşıp paylaşmadığını tespit eden bir sistemdir.

Asıl sorun: Sistem arka tarafta (backend) 3 tür şüpheli davranış tespit ederken, kullanıcı arayüzü (frontend) 7 farklı tür gösteriyor. Bu ikisi hiç eşleşmiyor. Yani ekrandaki istatistik kartları her zaman sıfır gösterecektir.

Neden önemli: Admin panelinden tarama yapıldığında sonuçlar yanlış gösteriliyor. Skorlar saniye formatında görünüyor ama aslında puan sistemi. Günlük istatistikler eksik veri gösteriyor. Kısacası modül çalışıyor gibi görünse de sunduğu bilgilerin çoğu yanlış veya eksik.

Tüm Bulgular Özet Tablosu

# Bulgu Önem Tür Konum
B1 Pattern Uyumsuzluğu (Backend 3 vs Frontend 7) KRİTİK Bug Service + Views
B2 formatScore() skoru saniye olarak gösteriyor (aslında puan) KRİTİK Bug index.blade.php:576
B3 getPatternCount() her zaman 3 döndürüyor KRİTİK Bug index.blade.php:592
B4 timelineData['overlaps'] key'i mevcut değil KRİTİK Bug show.blade.php:442
B5 daily_stats 'overlaps' key'i mevcut değil KRİTİK Bug show.blade.php:335
B6 scan_date format hatası (H:i her zaman 00:00) ORTA Bug show.blade.php:300
D1 'suspicious' durumu asla atanmıyor YÜKSEK Tasarım Service:122
D2 quickCheck() tanımlı ama hiç çağrılmıyor YÜKSEK Tasarım Controller + Job
D3 Ping-pong günlük istatistiği tarihe göre filtrelenmiyor ORTA Bug Service:576
D4 Model'deki abuseScoreFormatted accessor da saniye formatı kullanıyor ORTA Tasarım AbuseReport.php:195
P1 O(n²) performans (concurrent + split stream) ORTA Performans Service:277,345
E1 Otomatik tarama (cron/schedule) yok EKSIK Eksik -
E2 Export/İndirme özelliği yok (CSV/PDF) EKSIK Eksik -
E3 Rapor silme/arşivleme özelliği yok EKSIK Eksik -
E4 Tarama ilerleme durumu takibi yok (progress) EKSIK Eksik -
E5 show sayfası dark tema admin layout ile çakışıyor EKSIK UX show.blade.php

Kritik Buglar

B1 - KRİTİK

Pattern Uyumsuzluğu: Backend 3 Pattern, Frontend 7 Pattern

Backend (AbuseDetectionService)

ping_pong - A→B→A döngüsü
concurrent_different - Farklı cihaz aynı anda
split_stream - Aynı cihaz çakışma

Frontend (index + show blade)

rapid_skips - Var ama backend'de yok!
high_volume - Var ama backend'de yok!
repeat_songs - Var ama backend'de yok!
multi_device - Var ama backend'de yok!
suspicious_ip - Var ama backend'de yok!
no_sleep - Var ama backend'de yok!
bot_like - Var ama backend'de yok!

Sonuç:

  • index.blade.php'deki "Tespit Edilen Şüpheli Davranışlar" kartı her zaman gizli kalır (7 pattern'in hiçbiri veritabanına yazılmaz)
  • apiStats() controller'ında 7 pattern sayılmaya çalışılır ama hepsi her zaman 0 döner
  • show.blade.php'deki pattern kartları backend'den gelen 3 pattern'i patternMeta sözlüğünde bulamaz, fallback'e düşer
Etkilenen Dosyalar
AbuseDetectionService.php:158-200 → detectAllPatterns() sadece 3 key üretir
AbuseReportController.php:175-192 → apiStats() 7 farklı key arar
index.blade.php:60-113 → 7 pattern kartı göstermeye çalışır
show.blade.php:186-194 → patternMeta 7 key tanımlar
B2 - KRİTİK

formatScore() Skoru Saniye Olarak Gösteriyor (Aslında Puan)

Mevcut Kod (index.blade.php:576)

formatScore(seconds) {
    if (!seconds) return '0s';
    const m = Math.floor(seconds / 60);
    const s = seconds % 60;
    return m > 0 ? `${m}m ${s}s` : `${s}s`;
}

Olması Gereken

formatScore(score) {
    if (!score) return '0';
    return score.toLocaleString('tr-TR');
    // 300 → "300"
    // 1300 → "1.300"
}

Sorun: abuse_score puan bazlı (Ping-Pong: cycle×100, Concurrent: ×50, Split: ×30, max 1300). Ama formatScore() bunu saniye olarak gösteriyor. Skor 300 olan bir rapor ekranda "5m 0s" olarak görünüyor. Aynı hata Model'deki getAbuseScoreFormattedAttribute() accessor'ında da var.

B3 - KRİTİK

getPatternCount() Her Zaman 3 Döndürüyor

Mevcut Kod (index.blade.php:592)

getPatternCount(report) {
    if (!report.patterns_json) return 0;
    return Object.keys(report.patterns_json).length; // HER ZAMAN 3!
}

patterns_json her zaman 3 key içerir: ping_pong, concurrent_different, split_stream. Her birinin detected: true/false alanı var ama fonksiyon bunu kontrol etmiyor.

Doğru Implementasyon:

getPatternCount(report) {
    if (!report.patterns_json) return 0;
    return Object.values(report.patterns_json)
        .filter(p => p.detected === true).length;
}
B4 - KRİTİK

show.blade.php: timelineData['overlaps'] Key'i Mevcut Değil

View'da Kullanılan (show.blade.php)

$allOverlaps = $timelineData['overlaps'] ?? [];
$overlapIds = collect($allOverlaps)->flatMap(...);

Satır: 442-443, 661, 744

Service'in Döndürdüğü (getUserTimelineData)

return [
    'items' => $items,      // ✓
    'patterns' => $patterns, // ✓
    'stats' => [...]         // ✓
    // 'overlaps' YOK!
];

Sonuç: Detay sayfasındaki çakışma bölümü her zaman boş. Vis.js timeline'da çakışmalar vurgulanmıyor. "X ÇAKIŞMA TESPİT EDİLDİ" uyarısı asla görünmüyor. Gantt bar'lar overlap class'ı almıyor.

B5 - KRİTİK

daily_stats'ta 'overlaps' Key'i Yok

show.blade.php Bekliyor

$stat['overlaps'] // Kullanılan key

Satır 335, 430, 901 gibi yerlerde kullanılıyor

calculateDailyStats() Üretiyor

$dailyStats[$date] = [
    'plays' => ...,
    'desktop' => ...,
    'mobile' => ...,
    'ping_pong' => ...,
    'concurrent' => ...,
    'split_stream' => ...,
    // 'overlaps' KEY'İ YOK!
];

Çözüm: Ya calculateDailyStats()'a 'overlaps' => $concurrentCount + $splitCount ekle, ya da view'ları concurrent + split_stream key'lerini kullanacak şekilde güncelle.

B6 - ORTA

scan_date Format Hatası

show.blade.php:300'de $report->scan_date->format('d.m.Y H:i') kullanılıyor. Ama scan_date model'de 'date' olarak cast ediliyor (datetime değil). Bu yüzden saat kısmı her zaman "00:00" gösterecektir.

Düzeltme: $report->scan_date->format('d.m.Y') kullanılmalı.

Tasarım & Mantık Hataları

D1 - YÜKSEK

'suspicious' (Şüpheli) Durumu Asla Atanmıyor

scanUser() - Satır 120-122

$hasAbuse = $this->hasAnyPatternDetected($patterns);
$status = $hasAbuse ? AbuseReport::STATUS_ABUSE : AbuseReport::STATUS_CLEAN;
// ↑ SADECE "abuse" veya "clean"! "suspicious" HİÇ YOK!

Model'de determineStatus() metodu var ve threshold bazlı çalışıyor (300=şüpheli, 600=suistimal) ama hiç çağrılmıyor.

Frontend filtre dropdown'unda "Şüpheli" seçeneği var ama hiçbir kayıt bu durumda olmayacak.

Önerilen: hasAbuse yerine score bazlı determineStatus($abuseScore) kullanılmalı.

D2 - YÜKSEK

quickCheck() Tanımlı Ama Hiç Çağrılmıyor

quickCheck() tek fingerprint'li kullanıcıları erken filtrelemek için tasarlanmış. Job'un docblock'unda "Early Exit'i geçmiştir" yazıyor ama ne controller ne de Job bu fonksiyonu çağırıyor.

Akış Şu An:

Controller Dispatch Job scanUser()

Olması Gereken:

Controller quickCheck() Tek FP → Direkt CLEAN / Çoklu FP → Job

Performans etkisi: Tek cihazdan dinleyen kullanıcılar (çoğunluk) gereksiz yere Horizon'da job oluşturuyor.

D3 - ORTA

Ping-Pong Günlük İstatistiği Tarihe Göre Filtrelenmiyor

$pingPongCount = collect($patterns['ping_pong']['cycles'] ?? [])
    ->filter(fn($c) => true) // ← BU FİLTRE HİÇBİR ŞEY YAPMIYOR!
    ->count();

filter(fn($c) => true) tüm elemanları geçirir. Her gün için aynı toplam ping-pong sayısı yazılır. concurrent ve split_stream doğru şekilde tarihe göre filtreleniyor ama ping_pong değil.

Performans Sorunu

P1

O(n²) Algoritma: Concurrent & Split Stream Tespiti

Hem detectConcurrentDifferentSource() hem de detectSplitStream() iç içe for döngüsü kullanıyor (O(n²)).

Play Sayısı Karşılaştırma (Her pattern) Toplam (2 pattern)
1004,950~10K
500124,750~250K
1,000499,500~1M
5,00012,497,500~25M

Öneri: Zaman bazlı sıralama + sliding window yaklaşımı O(n²)'yi O(n log n)'e düşürür.

Eksik Özellikler

E1

Otomatik Tarama Yok

Tarama tamamen manuel (buton tıklaması). Günlük/haftalık cron schedule ile otomatik tarama yapılabilir.

E2

Export/İndirme Yok

CSV veya PDF olarak rapor indirme özelliği bulunmuyor. Raporları paylaşmak veya arşivlemek zor.

E3

Silme/Arşivleme Yok

Eski raporları temizleme veya arşivleme mekanizması yok. SoftDelete var ama kullanılmıyor.

E4

Tarama İlerleme Takibi Yok

"Tarama Başlat" tıklandığında job'lar dispatch edilir ama progress gösterilmez. Kaç kullanıcı tarandı, kaçı kaldı bilinmez.

E5

Dark Tema & Admin Layout Çakışması

show.blade.php özel dark tema kullanıyor ama admin layout muhtemelen Tabler.io light tema. CSS override'ları form kontrollerini bozabilir.

Teknik Detaylar: Dosya Haritası

Modules/Muzibu/
│─ App/Models/AbuseReport.php ← Model, cast, scopes, accessor'lar
│─ App/Services/AbuseDetectionService.php ← 3 pattern tespit motoru
│─ App/Http/Controllers/Admin/AbuseReportController.php ← 9 endpoint
│─ App/Jobs/ScanUserForAbuseJob.php ← Queue job (Horizon)
│─ resources/views/admin/abuse-reports/index.blade.php ← Liste (Alpine.js)
│─ resources/views/admin/abuse-reports/show.blade.php ← Detay (Vis.js + Alpine)
│─ routes/admin.php ← 9 route (satır 290-335)
├─ database/migrations/tenant/2025_12_29_*.php ← Tablo oluşturma

Önerilen Düzeltme Sırası

1
B1 + B3: Pattern uyumsuzluğunu çöz. Ya backend'e 7 pattern ekle, ya frontend'i 3 pattern'e uyarla.
2
B2 + D4: formatScore() ve Model accessor'ını puan formatına çevir.
3
B4 + B5: getUserTimelineData()'ya overlaps key'i ekle, daily_stats'a overlaps key'i ekle.
4
D1: Status belirleme mantığını determineStatus() ile değiştir (suspicious seviyesi aktif olsun).
5
D2: quickCheck() entegrasyonunu Job'a ekle (performans iyileştirme).
6
B6, D3, P1: Küçük fix'ler: tarih formatı, ping-pong daily stats, performans optimizasyonu.