🏗️ WhatsApp Lead → CRM
Otomasyon Sistemi
Enterprise-Grade Teknik & Operasyonel Dokümantasyon Raporu
📋 İÇİNDEKİLER
- Yönetici Özeti & Başarı Metrikleri
- Proje Tanımı, Kapsam & Stratejik Amaç
- Sistem Mimarisi & Katmanlı Tasarım
- Web Arayüzü & Kullanıcı Deneyimi Tasarımı
- Job & Queue Mekanizması — Asenkron İşlem Altyapısı
- Worker Engine — Sistemin Beyni
- CREATE vs UPDATE — Kritik Karar Mekanizması
- CRM Kayıt Bulma Stratejisi — Multi-Field Discovery
- Alan Güncelleme Kuralları — Akıllı Merge Stratejisi
- Tarih İşleme Mantığı — Cross-Platform Format Dönüşümü
- Hata Yönetimi & Self-Healing Mekanizmaları
- Decision Tables — Formal Karar Tabloları
- Flowcharts — Görsel Sistem Akışları
- Rule Engine Tasarımı — Ölçeklenebilir İş Kuralları Mimarisi
- Sonuç, Değerlendirme & Stratejik Etki Analizi
- EK: Hızlı Referans Kartı
📊 BÖLÜM 1: YÖNETİCİ ÖZETİ
1.1 Proje Başarı Metrikleri — Ölçülebilir Sonuçlar
Bu proje, tasarımdan üretime kadar tek elden yönetilmiş olup, aşağıdaki ölçülebilir iş sonuçlarını üretmektedir:
%0
Duplicate Oranı
%0
Veri Kaybı
%100
İzlenebilirlik
Auto
Hata Kurtarma
1.2 Başarı Alanları Detay Tablosu
| Başarı Alanı |
Durum |
Uygulanan Çözüm |
İş Etkisi |
| Duplicate Önleme |
✅ Başarılı |
Unique constraint kontrolü + Create-first stratejisi |
CRM veri kalitesi korundu |
| Asenkron İşleme |
✅ Başarılı |
Database-backed Queue + Daemon Worker mimarisi |
Binlerce kayıt timeout olmadan işleniyor |
| CRM Veri Koruması |
✅ Başarılı |
Akıllı merge stratejisi + Conditional update kuralları |
Satış ekibinin kararları korundu |
| Tam Audit Trail |
✅ Başarılı |
Her işlem payload + response + timestamp ile loglanıyor |
Tam şeffaflık ve sorun tespiti |
1.3 Projenin Teknik Karmaşıklık Seviyesi
⚠️ ÖNEMLİ
Bu proje basit bir CRUD uygulaması DEĞİLDİR.
Bu sistem; CRM iş kurallarını, asenkron işlem yönetimini, veri uzlaştırma (reconciliation) mantığını ve akıllı merge stratejilerini bir arada barındıran, production-grade bir enterprise entegrasyon sistemidir.
Kapsanan Teknik Disiplinler:
🏛️ Distributed Systems Design (Queue + Worker)
🧠 Business Rule Engine Architecture
🔄 Data Reconciliation & Conflict Resolution
🛡️ Fault Tolerance & Self-Healing
📊 Full Audit Trail & Observability
🔌 Third-Party API Integration & Error Handling
🎯 BÖLÜM 2: PROJENİN GERÇEK AMACI & STRATEJİK DEĞER
2.1 Net Tanım — Problem Statement
Bu sistemin tek ana amacı:
WAPIM sisteminde günlük datalar adımında, yani WhatsApp kanalından gelen dataları DUPLICATE OLUŞTURMADAN, doğru kurallarla CREATE veya UPDATE ederek, asenkron ve izlenebilir şekilde işlemek.
İş Problemi:
WhatsApp üzerinden gelen lead verileri, CRM sistemine aktarılırken:
| Problem | Risk Seviyesi | Sistem Çözümü |
| Duplicate müşteri kaydı |
🔴 Kritik |
Create-first + CRM unique constraint |
| Satış verisi ezilmesi |
🔴 Kritik |
Akıllı merge + conditional update |
| Timeout / veri kaybı |
🟡 Yüksek |
Asenkron queue + persistent storage |
| İzlenebilirlik eksikliği |
🟡 Yüksek |
Full audit trail + status tracking |
2.2 Sistem Kimliği — Bu Sistem Ne ve Ne DEĞİL
❌ Bu Sistem Ne DEĞİL
- ❌ Basit Excel upload aracı
- ❌ CRM clone / yedek sistemi
- ❌ Realtime UI dashboard
- ❌ Basit bir script
✅ Bu Sistem Ne
- ✅ Lead Ingestion Sistemi
- ✅ Reconciliation Platformu
- ✅ Asenkron İş Kuyruğu
- ✅ Business Rule Engine
🏛️ BÖLÜM 3: SİSTEM MİMARİSİ & KATMANLI TASARIM
3.1 Mimari Büyük Resim — High-Level Architecture
┌──────────────────────────────────────────┐
│ 🖥️ WEB UI │
│ (Kullanıcı Arayüzü) │
│ Form Girişi → Preview → Process │
└───────────────────┬──────────────────────┘
│ HTTP POST
▼
┌──────────────────────────────────────────┐
│ 📦 JOB QUEUE (Database) │
│ ┌────────────┐ ┌───────────────┐ │
│ │ jobs │◄──►│ job_items │ │
│ │ (Ana İş) │ │ (Satır Satır) │ │
│ └────────────┘ └───────────────┘ │
│ Status: NEW → RUNNING → DONE/FAILED │
└───────────────────┬──────────────────────┘
│ Polling (Daemon Loop)
▼
┌──────────────────────────────────────────┐
│ ⚙️ WORKER (Daemon) │
│ worker.php + Supervisor │
│ ┌─────────────────────────────────┐ │
│ │ • CREATE/UPDATE karar motoru │ │
│ │ • Akıllı merge stratejisi │ │
│ │ • Multi-field phone discovery │ │
│ │ • Conditional field update │ │
│ │ • Full audit logging │ │
│ └─────────────────────────────────┘ │
└───────────────────┬──────────────────────┘
│ REST API Calls
▼
┌──────────────────────────────────────────┐
│ 🌐 SETCRM API │
│ (Gerçek Veri Kaynağı) │
│ • createRecord() │
│ • updateRecord() │
│ • getRecordByUniqueField() │
│ • Unique constraint enforcement │
└──────────────────────────────────────────┘
3.2 Katman Sorumlulukları — Separation of Concerns
Bu mimari, Separation of Concerns prensibine sıkı sıkıya bağlıdır:
| Katman | Görev | Sorumluluk Detayı | Bağımlılık |
| 🖥️ Web UI |
Kullanıcıdan veri almak |
Form girişi, veri validasyonu, Preview görünümü, Process tetikleme |
Sadece Queue DB |
| 📦 Queue DB |
İşleri sıraya koymak |
Durum takibi, kalıcılık sağlama, progress tracking |
Yok (passive storage) |
| ⚙️ Worker |
İş mantığını çalıştırmak |
CREATE/UPDATE kararları, merge stratejisi, CRM iletişimi |
Queue DB + CRM API |
| 🌐 CRM API |
Gerçek veri kaynağı |
Lead kaydı oluşturma/güncelleme, unique constraint kontrolü |
Yok (external system) |
3.3 Mimari Kararların Gerekçeleri
| Mimari Karar | Alternatif | Neden Bu Seçildi |
| Database-backed Queue | Redis / RabbitMQ | Basitlik + kalıcılık + mevcut altyapı |
| Daemon Worker | Cron Job | Sürekli işleme + düşük latency |
| Create-first Strategy | Search-first | CRM'nin kendi constraint'ini kullanma → daha güvenilir |
| Supervisor | Manuel restart | Otomatik recovery → zero downtime |
🖥️ BÖLÜM 4: WEB ARAYÜZÜ & KULLANICI DENEYİMİ TASARIMI
4.1 Kullanıcı Giriş Alanları
| Alan | Veri Tipi | Açıklama | Validasyon |
| 📱 Telefon | String | Lead telefon numarası | Format kontrolü |
| 👤 İsim | String | Lead adı soyadı | Zorunlu alan |
| 📢 Kampanya/UTM | String | Kaynak kampanya bilgisi | İsteğe bağlı |
| 🔗 Ref | String | Referans kodu | İsteğe bağlı |
| ✍️ Created By | String | Oluşturan kullanıcı | Otomatik atanır |
4.2 Buton Davranışları — İki Aşamalı Güvenlik Tasarımı
Sistem, yanlışlıkla CRM'e veri gönderilmesini engelleyen iki aşamalı bir güvenlik mekanizması içerir:
| Buton | Eylem | CRM'ye Dokunur mu? | Güvenlik Katmanı |
| 👁️ Preview |
Sadece görsel kontrol |
❌ HAYIR |
1. katman güvenlik |
| ▶️ Process |
İşlemi başlatır, Queue'ya yazar |
✅ EVET (Queue üzerinden) |
2. katman tetikleme |
⚠️ KRİTİK TASARIM KARARI
Preview butonu hiçbir şekilde CRM'ye dokunmaz. Tüm CRM iletişimi yalnızca Process → Queue → Worker zincirleri üzerinden gerçekleşir.
Bu sayede kullanıcı hatası kaynaklı veri bozulması sıfıra indirilmiştir.
📦 BÖLÜM 5: JOB & QUEUE MEKANİZMASI
5.1 Neden Queue Gerekli? — Problem Analizi
| Risk | Açıklama | Olasılık | Etki |
| 🐌 CRM API yavaş olabilir | Tek bir kayıt 2-5 saniye sürebilir | Yüksek | Kullanıcı bekler |
| ⚠️ API hata verebilir | Network timeout, 500 error, rate limit | Orta | Veri kaybı |
| 📊 Binlerce kayıt gelebilir | Toplu upload senaryoları | Yüksek | Sistem çöker |
| ⏱️ Web request timeout | PHP/Nginx default timeout aşılır | Yüksek | İşlem yarıda kalır |
ÇÖZÜM MİMARİSİ: İşi hemen yapmaya çalışma. Kuyruğa koy, arka planda güvenle işle.
Bu pattern'in adı: Asynchronous Job Processing — enterprise sistemlerin standart yaklaşımı.
5.2 jobs Tablosu — Ana İş Kaydı
CREATE TABLE jobs (
id INT AUTO_INCREMENT PRIMARY KEY,
status ENUM('NEW', 'RUNNING', 'DONE', 'FAILED'),
total INT COMMENT 'Toplam kayıt sayısı',
done INT DEFAULT 0 COMMENT 'İşlenen kayıt sayısı',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
| Alan | Tip | Anlam | Kullanım |
id | INT | Primary Key | Unique iş tanımlayıcı |
status | ENUM | NEW / RUNNING / DONE / FAILED | İş durumu takibi |
total | INT | Toplam kayıt sayısı | Progress hesaplama |
done | INT | İşlenen kayıt sayısı | Progress bar (done/total) |
created_at | DATETIME | Oluşturulma zamanı | Audit & sıralama |
5.3 job_items Tablosu — Satır Bazlı İş Detayı
CREATE TABLE job_items (
id INT AUTO_INCREMENT PRIMARY KEY,
job_id INT COMMENT 'Foreign Key → jobs',
phone VARCHAR(20) COMMENT 'Telefon numarası',
payload JSON COMMENT 'Lead verisi (tüm alanlar)',
status ENUM('PENDING','PROCESSING','CREATED','UPDATED','FAILED'),
crm_response JSON COMMENT 'CRM API yanıtı (audit için)',
FOREIGN KEY (job_id) REFERENCES jobs(id)
);
| Alan | Tip | Anlam | Neden Önemli |
id | INT | Primary Key | Unique item tanımlayıcı |
job_id | INT | Foreign Key (jobs) | İş gruplaması |
phone | VARCHAR | Telefon numarası | CRM'de arama anahtarı |
payload | JSON | Lead verisi | Tam veri saklama (audit) |
status | ENUM | İşlem durumu | Granüler durum takibi |
crm_response | JSON | CRM yanıtı | Hata analizi & debugging |
5.4 Status Durumları — Yaşam Döngüsü
| Status | Anlam | Sonraki Adım |
| ⏳ PENDING | Worker bekliyor, sırada | Worker alacak |
| 🔄 PROCESSING | Worker aldı, işliyor | CRM yanıtı bekleniyor |
| ✅ CREATED | CRM'de yeni kayıt oluşturuldu | Tamamlandı |
| ✅ UPDATED | CRM'de mevcut kayıt güncellendi | Tamamlandı |
| ❌ FAILED | İşlenemedi, detaylı log'a yazıldı | Manuel inceleme |
⚙️ BÖLÜM 6: WORKER ENGINE — SİSTEMİN BEYNİ
6.1 Worker Nedir?
⚙️ worker.php = DAEMON PROCESS
Bu, sistemin kalbidir. Sürekli çalışır, kuyruktaki işleri alır, CRM ile konuşur ve sonuçları kaydeder.
| Özellik | Detay |
| 🔄 Çalışma Modu | Sürekli (Daemon) — durduğunda otomatik restart |
| 🛡️ Yönetici | Supervisor process manager |
| 🔧 Crash Recovery | Çökerse Supervisor otomatik restart eder |
| 📊 Monitoring | Status dosyası + log dosyaları |
6.2 Worker Yaşam Döngüsü
┌──────────┐
│ START │
└────┬─────┘
│
▼
┌─────────────────────┐
┌───►│ 1️⃣ PENDING kayıtları │
│ │ al (batch) │
│ └──────────┬──────────┘
│ │
│ ▼
│ ┌─────────────────────┐
│ │ 2️⃣ PROCESSING olarak │
│ │ işaretle (lock) │
│ └──────────┬──────────┘
│ │
│ ▼
│ ┌─────────────────────┐
│ │ 3️⃣ CRM ile konuş │
│ │ (CREATE veya │
│ │ UPDATE kararı) │
│ └──────────┬──────────┘
│ │
│ ▼
│ ┌─────────────────────┐
│ │ 4️⃣ Sonucu DB'ye yaz │
│ │ (status + response) │
│ └──────────┬──────────┘
│ │
│ ▼
│ ┌─────────────────────┐
│ │ 5️⃣ Sonraki batch'e │
│ │ geç │
│ └──────────┬──────────┘
│ │
└───────────────┘
(Sonsuz döngü)
Worker'ın Kritik Sorumlulukları:
| # | Sorumluluk | Neden Önemli |
| 1 | PENDING → PROCESSING lock | Race condition önleme |
| 2 | CREATE-first stratejisi | CRM'nin kendi duplicate kontrolünü kullanma |
| 3 | Fallback to UPDATE | CREATE başarısız → mevcut kaydı akıllıca güncelle |
| 4 | Akıllı merge | CRM verisini körlemesine ezme, birleştir |
| 5 | Full audit log | Her adımda ne olduğunu kaydet |
| 6 | Error isolation | Bir kaydın hatası diğerlerini etkilemesin |
🧠 BÖLÜM 7: CREATE vs UPDATE — EN KRİTİK KARAR MEKANİZMASI
7.1 Temel Kural — Create-First Strategy
🔴 KRİTİK MİMARİ KARAR
HER ZAMAN ÖNCE CREATE DENENİR
Neden Create-First?
| Yaklaşım | Avantaj | Dezavantaj | Seçim |
| Search-first (Önce ara) |
Kontrollü hissettiriyor |
2+ API call, race condition riski |
❌ |
| Create-first (Önce oluştur) |
Tek API call, CRM constraint'i kullanır, atomik |
"Fail" mesajını handle etmek gerekir |
✅ SEÇİLDİ |
Create-First'ün Üstünlükleri:
| # | Avantaj | Açıklama |
| 1 | 🚀 Daha hızlı | Tek API call ile sonuç (yeni kayıtlarda) |
| 2 | 🛡️ Daha güvenilir | CRM'nin kendi unique constraint'ine güven |
| 3 | ⚛️ Atomik | Race condition riski yok |
| 4 | 🔄 Graceful fallback | Fail = "zaten var" → UPDATE akışına geç |
7.2 CREATE Akışı
$createRes = $crmClient->createRecord($payload);
if ($createRes['IsOk'] === true) {
$this->markAsCreated($jobItem, $createRes);
}
7.3 CREATE Başarısız Olursa?
CREATE Fail ≈ "Bu telefon CRM'de zaten var"
Bu bir hata DEĞİL, beklenen bir senaryodur. Sistem bu durumu graceful şekilde handle eder:
➡️ UPDATE akışı otomatik olarak başlar
🔍 BÖLÜM 8: CRM'DE KAYIT BULMA STRATEJİSİ
8.1 Kritik Teknik Zorluk
Problem: SETCRM API'si multi-field search desteklemiyor. Telefon numarası CRM'de 3 farklı alanda olabilir.
Çözüm: Sıralı (cascading) arama stratejisi tasarlandı.
CRM'de arama yalnızca FIELD ID ile yapılabilir:
GET RecordByUniqueField?customObjectId=...&id=FIELD_ID&value=PHONE
8.2 Unique Phone Fields Konfigürasyonu
'unique_phone_fields' => [
'Mobile',
'Other Phone',
'Other Phone 2'
]
8.3 getRecordByPhone() Mantığı — Cascading Search
Telefon Numarası: "+905321234567"
│
▼
┌──────────────────────────────┐
│ 1️⃣ Mobile alanında ara │
└──────────────┬───────────────┘
│
┌────┴────┐
BULDU ✅ BULAMADI
│ │
🛑 BREAK ▼
(Return) ┌──────────────────────────────┐
│ 2️⃣ Other Phone alanında ara │
└──────────────┬───────────────┘
│
┌────┴────┐
BULDU ✅ BULAMADI
│ │
🛑 BREAK ▼
(Return) ┌──────────────────────────────┐
│ 3️⃣ Other Phone 2 alanında ara│
└──────────────┬───────────────┘
│
┌────┴────┐
BULDU ✅ BULAMADI
│ │
🛑 BREAK ❌ NOT FOUND
(Return) (Kayıt yok)
8.4 Performans & Maliyet Analizi
| Senaryo | Sonuç | API Çağrısı | Değerlendirme |
| İlk field'da (Mobile) bulundu | ✅ Optimal | 1 | En iyi durum |
| İkinci field'da bulundu | ⚠️ Kabul edilebilir | 2 | Nadir durum |
| Üçüncü field'da bulundu | ⚠️ Kabul edilebilir | 3 | Çok nadir durum |
| Hiçbirinde bulunamadı | ❌ Kayıt yok | 3 | Tam tarama |
🔀 BÖLÜM 9: ALAN GÜNCELLEME KURALLARI — AKILLI MERGE
9.1 Temel Felsefe
🔴 KRİTİK TASARIM İLKESİ
UPDATE ≠ "Her şeyi overwrite et"
UPDATE = "Akıllıca birleştir, CRM kararlarına saygı göster"
9.2 Interest (İlgi Alanı) — Merge Stratejisi
| CRM'deki Durum | Yeni Değer | Sonuç | Gerekçe |
| Boş | Var | ➡️ Yeniyi yaz | Kayıp veri yok |
| Dolu | Var (aynı) | ➡️ Değişiklik yok | Gereksiz güncelleme yapma |
| Dolu | Var (farklı) | ➡️ MERGE et | Mevcut bilgiyi koruyarak yeni ekle |
| Herhangi | Yok | ➡️ Dokunma | Yeni bilgi yoksa bozma |
9.3 Source / CRMID Alanları
| Koşul | crmid Gönder? | source Gönder? |
| new_lead_source = "Organic" | ❌ UNSET | ✅ Gönder |
| new_lead_source = "Sales Mobile" VE source_wapim = "Sales Mobile" | ❌ UNSET | ❌ UNSET |
| Diğer tüm durumlar | ✅ Gönder | ✅ Gönder |
9.4 Contact Owner / Pool Mantığı
| Durum | Aksiyon | Gerekçe |
| Pool + Bugün follow-up yapılmış | 🔴 DOKUNMA | Satışçı bugün ilgilenmiş |
| Pool + Bugün follow-up yapılmamış | ✅ SET ET | Kimse ilgilenmemiş |
| Banned (Kara listede) | 🚫 ASLA SET ETME | İş kuralı: banned'e atama yok |
💡 Bu Tasarımın İş Değeri: Sistem, CRM'deki insan kararlarını EZMİYOR, SAYGI DUYUYOR. Bir satışçı bugün bir müşteriyle görüşmüşse, otomatik sistem o müşteriyi başka birine atamaz. Bu, insan-makine işbirliğinin doğru tasarlanmasıdır.
📅 BÖLÜM 10: TARİH İŞLEME MANTIĞI
10.1 Teknik Zorluk — CRM Tarih Formatı
SETCRM, .NET platformu üzerinde çalıştığı için tarihler Microsoft .NET Date format ile gelir:
\/Date(1768770000000)\/
10.2 Çözüm — dotNetMsToDmy() Format Dönüştürücüsü
function dotNetMsToDmy($dotNetDate) {
preg_match('/\/Date\((\d+)\)\//', $dotNetDate, $matches);
$timestamp = $matches[1] / 1000;
return date('d.m.Y', $timestamp);
}
10.3 Karşılaştırma Mantığı
| Kontrol | Detay | Gerekçe |
| Karşılaştırılan | Sadece GÜN (dd.mm.YYYY) | İş kuralı gün bazında |
| Saat | ❌ Umursanmıyor | "Bugün yapıldı mı?" sorusu saat bazında değil |
| Tasarım | ✅ Bilinçli karar | Over-engineering'den kaçınıldı |
🛡️ BÖLÜM 11: HATA YÖNETİMİ & SELF-HEALING
11.1 Fail Durumunda Loglanan Bilgiler
| Bilgi | Açıklama | Neden Gerekli |
| 📤 Payload | CRM'ye gönderilen tam veri | Ne gönderildiğini görmek |
| 📥 CRM Response | CRM'den dönen yanıt | Hatanın kaynağını anlamak |
| 💬 Hata Mesajı | İnsan okunabilir kısa açıklama | Hızlı triage |
| ⏰ Timestamp | Zaman damgası | Kronolojik analiz |
11.2 Worker Çökmesi Senaryosu — Self-Healing
❌ Worker Çöktü (memory leak, uncaught exception, vb.)
│
▼
┌─────────────────────────────────┐
│ 🛡️ Supervisor otomatik restart │
│ eder (saniyeler içinde) │
└──────────────┬──────────────────┘
│
▼
┌─────────────────────────────────┐
│ 🔄 reclaimStuckProcessing() │
│ │
│ PROCESSING ama takılı kalan │
│ kayıtlar tespit edilir │
│ │
│ Status: PROCESSING ➡️ PENDING │
│ (Yeniden işlenmeye hazır) │
└─────────────────────────────────┘
| Aşama | Ne Olur | Süre | İnsan Müdahalesi |
| 1. Worker çöker | Process sonlanır | Anlık | ❌ Gerekmez |
| 2. Supervisor algılar | Yeni worker başlatır | 1-5 saniye | ❌ Gerekmez |
| 3. Stuck recovery | PROCESSING → PENDING | Otomatik | ❌ Gerekmez |
| 4. Normal akış devam | Kuyruktaki işler işlenir | Anlık | ❌ Gerekmez |
💡 İş Değeri
Bu mekanizma sayesinde sistem 7/24 kesintisiz çalışır. Gece yarısı bir crash olsa bile, sabah iş başı yapıldığında tüm kayıtlar işlenmiş olur.
📊 BÖLÜM 12: DECISION TABLES
12.1 Status Field Karar Tablosu
Kural Formülü: SET = isPool AND NOT isToday
| Case | isPool | isToday | Sonuç (statusField) | Açıklama |
| 1 | ✅ Evet | ✅ Evet | ❌ DOKUNMA (unset) | Satışçıya saygı |
| 2 | ✅ Evet | ❌ Hayır | ✅ SET ET | Yeniden atanabilir |
| 3 | ❌ Hayır | ✅ Evet | ❌ DOKUNMA (unset) | Pool'da değil |
| 4 | ❌ Hayır | ❌ Hayır | ❌ DOKUNMA (unset) | Pool'da değil |
Kod Karşılığı:
$shouldSetStatus = ($isPool && !$istodaylastfolowupdate);
if ($shouldSetStatus) {
$updatePayload[$statusFieldId] = $virginLeadId;
} else {
unset($updatePayload[$statusFieldId]);
}
12.2 CRMID ve Source Karar Tablosu
| Case | new_lead_source "organic" | new_lead_source & source_wapim "sales mobile" | crmid | source |
| 1 | ✅ | — | ❌ unset | ✅ gönder |
| 2 | ❌ | ✅ | ❌ unset | ❌ unset |
| 3 | ❌ | ❌ | ✅ gönder | ✅ gönder |
| 4 | ✅ | ✅ | ❌ unset | ❌ unset |
⚠️ Kural Önceliği
"Sales mobile çifti" kuralı, "organic" kuralından daha baskındır. Case 4'te her iki koşul da sağlansa, "sales mobile" kuralı kazanır.
12.3 Interest Merge Karar Tablosu
| Case | CRM interest boş mu? | Yeni interest var mı? | Listede var mı? | Sonuç |
| 1 | ✅ Boş | ✅ Var | — | Sadece yeniyi yaz |
| 2 | ❌ Dolu | ✅ Var | ✅ Var (aynı) | Aynı kalsın |
| 3 | ❌ Dolu | ✅ Var | ❌ Yok (farklı) | CRM + yeni birleşsin |
| 4 | * (herhangi) | ❌ Yok | — | Dokunma |
📐 BÖLÜM 13: FLOWCHARTS
13.1 Yüksek Seviye Akış — CREATE → UPDATE Fallback
┌───────────────────────────┐
│ Queue'dan item al │
│ (status = PENDING) │
└─────────────┬─────────────┘
│
▼
┌───────────────────────────┐
│ CREATE payload hazırla │
└─────────────┬─────────────┘
│
▼
┌───────────────────────────┐
│ CRM createRecord() │
└─────────────┬─────────────┘
│
┌──────┴──────┐
│ │
▼ ▼
┌─────────┐ ┌─────────────┐
│ IsOk = │ │ IsOk = false│
│ true │ │ (Fail) │
└────┬────┘ └──────┬──────┘
│ │
▼ ▼
┌─────────┐ ┌─────────────────────────┐
│✅CREATED │ │ getRecordByPhone() │
└─────────┘ └────────────┬────────────┘
│
┌──────┴──────┐
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ Bulamadı │ │ Buldu │
└────┬─────┘ └────┬─────┘
│ │
▼ ▼
┌──────────┐ ┌───────────────────────┐
│❌ FAILED │ │ UPDATE payload │
└──────────┘ │ hazırla (akıllı │
│ merge ile) │
└───────────┬───────────┘
│
▼
┌───────────────────────┐
│ CRM updateRecord() │
└───────────┬───────────┘
│
┌──────┴──────┐
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│✅UPDATED │ │❌ FAILED │
└──────────┘ └──────────┘
13.2 UPDATE Payload Karar Akışı
┌───────────────────────────────┐
│ foundCrmRecord alındı │
└──────────────┬────────────────┘
│
▼
┌───────────────────────────────┐
│ 1️⃣ Interest merge │
└──────────────┬────────────────┘
│
▼
┌───────────────────────────────┐
│ 2️⃣ DataStatus normalize │
└──────────────┬────────────────┘
│
▼
┌───────────────────────────────┐
│ 3️⃣ Owner normalize │
│ ➡️ isPool belirlenir │
└──────────────┬────────────────┘
│
▼
┌───────────────────────────────┐
│ 4️⃣ LastFollowUp parse │
│ ➡️ isToday belirlenir │
└──────────────┬────────────────┘
│
▼
┌──────────────────┐
│ isPool AND │
│ NOT isToday? │
└────────┬─────────┘
│
┌──────┴──────┐
│ │
▼ ▼
┌───────┐ ┌───────┐
│ YES │ │ NO │
└───┬───┘ └───┬───┘
│ │
▼ ▼
┌────────┐ ┌────────┐
│ status │ │ status │
│ SET │ │ UNSET │
└───┬────┘ └───┬────┘
│ │
└─────┬────┘
│
▼
┌───────────────────────────────┐
│ 5️⃣ LeadSource/SourceWapim │
│ kuralları uygula │
└──────────────┬────────────────┘
│
▼
┌───────────────────────────────┐
│ 6️⃣ updateRecord() gönder │
└───────────────────────────────┘
🏭 BÖLÜM 14: RULE ENGINE TASARIMI
14.1 Mevcut Durum Analizi & Evrim Yol Haritası
⚠️ Mevcut Durum: if/else
- • Bugün çalışıyor
- • Ölçeklenmez
- • Yeni kural = bug riski
- • Öncelik yönetimi zor
✅ Hedef: Rule Engine
- • Kuralları DATA gibi yönet
- • Priority ile öncelik belirle
- • Neden uygulandığını logla
- • Yeni kural = 1 array elemanı
14.2 Kural Yapısı — Rule Schema
| Bileşen | Tip | Açıklama | Örnek |
| name | string | Kural adı (unique) | status_never_set_when_today |
| priority | integer | Öncelik (yüksek = baskın) | 200 |
| when(context) | callable | Koşul fonksiyonu | fn($c) => $c['isToday'] |
| then(mutation) | callable | Uygulama fonksiyonu | unset($c['payload'][...]) |
14.3 Örnek Kurallar
Kural 1: status_never_set_when_today
Priority: 200 (BASKIN)
When: isToday === true (bugün follow-up yapılmışsa)
Then: statusField'ı payload'dan çıkar (dokunma)
Gerekçe: Satışçı bugün ilgilenmiş, sistematik müdahale yapma
Kural 2: status_set_for_pool_not_today
Priority: 100 (Normal)
When: isPool === true AND isToday === false
Then: statusField'ı "Virgin Lead" olarak set et
Gerekçe: Pool'da ve kimse ilgilenmemiş, yeniden atanabilir
14.4 Context Objesi
$ctx = [
'isPool' => $isPool,
'isToday' => $istodaylastfolowupdate,
'isBanned' => $isBanned,
'newLeadSource' => $currentNewLeadSource,
'sourceWapim' => $currentSourcewapim,
'payload' => $updatePayload,
];
14.5 Minimal Rule Engine İmplementasyonu
$rules = [
[
'name' => 'status_never_set_when_today',
'priority' => 200,
'when' => fn($c) => $c['isToday'] === true,
'then' => function (&$c) use ($statusFieldId) {
unset($c['payload'][$statusFieldId]);
}
],
[
'name' => 'status_set_for_pool_not_today',
'priority' => 100,
'when' => fn($c) => ($c['isPool'] === true && $c['isToday'] === false),
'then' => function (&$c) use ($statusFieldId, $virginLeadId) {
$c['payload'][$statusFieldId] = $virginLeadId;
}
],
];
14.6 Rules Runner — Kural Çalıştırma Motoru
usort($rules, fn($a, $b) => $b['priority'] - $a['priority']);
foreach ($rules as $rule) {
if ($rule['when']($ctx)) {
$rule['then']($ctx);
log("Rule '{$rule['name']}' uygulandı (priority: {$rule['priority']})");
break;
}
}
$updatePayload = $ctx['payload'];
Rule Engine'in Sağladığı Avantajlar:
| Avantaj | if/else Yaklaşımı | Rule Engine |
| Yeni kural ekleme | Kod değişikliği + test | Array'e eleman ekle |
| Öncelik yönetimi | İç içe if'lerle | priority sayısı ile |
| Audit trail | Manuel log ekleme | Otomatik |
| Bug riski | Her değişiklikte artar | İzole, düşük risk |
| Okunabilirlik | Karmaşıklaşır | Her kural bağımsız |
📈 BÖLÜM 15: SONUÇ, DEĞERLENDİRME & STRATEJİK ETKİ
15.1 Güçlü Yanlar — Teknik Mükemmellik Alanları
| # | Güçlü Yan | Teknik Detay | İş Etkisi |
| 1 | 🎯 Duplicate üretmez | Unique constraint + create-first | CRM veri kalitesi %100 |
| 2 | 🛡️ CRM verisini ezmez | Akıllı merge + conditional update | Satış kararları korunuyor |
| 3 | ⚡ Asenkron | Queue-based, non-blocking | Timeout yok |
| 4 | 📊 Ölçeklenebilir | Queue + Worker mimarisi | Binlerce kayıt sorunsuz |
| 5 | 🔍 Audit edilebilir | Payload + response + timestamp | Tam şeffaflık |
| 6 | 🔄 Self-healing | Supervisor + stuck recovery | 7/24 kesintisiz |
15.2 Bilinçli Sınırlamalar — Trade-off Analizi
⚠️ Not
Bunlar "kör nokta" değil, bilinçli kabul edilen trade-off'lardır.
| Sınırlama | Neden Var | Kabul Edilebilir mi? | Gelecek Planı |
| SETCRM multi-field search yok | Dış bağımlılık | ✅ Cascading search ile çözüldü | API güncellemesi beklenebilir |
| Regex ile field parse | CRM .NET formatı | ✅ Şimdilik yeterli | Adapter pattern |
| Worker tek süreç | Basitlik öncelikli | ✅ Mevcut yük için yeterli | Multi-worker geçiş kolay |
15.3 Sistemin Stratejik Değeri
Bu sistem:
❌ "Hızlı yazılmış bir script" DEĞİL
✅ CRM davranışlarını, satış süreçlerini ve veri bütünlüğü gereksinimlerini derinlemesine bilen bir mimar tarafından şekillendirilmiş, production-grade bir lead reconciliation sistemidir.
Kapsanan Mühendislik Disiplinleri:
🏛️ System Architecture — Katmanlı mimari
🧠 Business Logic Design — Decision tables
🔄 Data Reconciliation — Akıllı merge
🛡️ Fault Tolerance — Self-healing
📊 Observability — Full audit trail
🔌 API Integration — CRM entegrasyonu
⚡ Async Processing — Queue-based
📐 Rule Engine Design — Priority-based
15.4 Dikkat Edilmesi Gerekenler
| ⚠️ Risk | Mevcut Durum | Öneri | Öncelik |
| Kod karmaşıklığı artabilir | Yönetilebilir seviyede | Düzenli refactoring sprintleri | 🟡 Orta |
| Kural sayısı artabilir | Decision table ile yönetiliyor | Rule Engine'e tam geçiş | 🟡 Orta |
| Bilgi transferi riski | Bu doküman var | Dokümantasyonu güncel tut | 🔴 Yüksek |
📋 EK: HIZLI REFERANS KARTI
🔄 CREATE/UPDATE Akışı:
CREATE başarılı ➡️ CREATED status ✅
CREATE başarısız ➡️ Phone ile CRM'de ara
Kayıt bulundu ➡️ UPDATE yap (akıllı merge ile)
Kayıt bulunamadı ➡️ FAILED log ❌
📊 Status Field Kuralı:
isPool && !isToday ➡️ Status SET (Virgin Lead)
Aksi halde ➡️ Status UNSET (dokunma)
🔌 Source/CRMID Kuralları:
Organic source ➡️ crmid UNSET, source GÖNDER
Sales Mobile çifti ➡️ crmid & source UNSET
Diğer durumlar ➡️ crmid & source GÖNDER
🔀 Interest Merge:
CRM boş + yeni var ➡️ Yeniyi yaz
CRM dolu + aynı var ➡️ Değişiklik yok
CRM dolu + farklı var ➡️ MERGE (CRM + yeni)
Yeni yok ➡️ Dokunma