[DRY-RUN] Found {$count} pending payout requests in queue.\n"; $results['payouts'] = "Dry-run: {$count} pending"; } else { $payoutResult = CliqPaymentService::processPayoutQueue(); echo " -> {$payoutResult['message']}\n"; $results['payouts'] = $payoutResult; } } catch (\Throwable $e) { echo " -> ERROR: " . $e->getMessage() . "\n"; $results['payouts'] = 'Error: ' . $e->getMessage(); } } // ───────────────────────────────────────────────────────────────────────────── // 2. تدقيق الترفيع وقفل الدروس بحسب الصف الحالي (Grade Progression Access Audit) // ───────────────────────────────────────────────────────────────────────────── if ($task === 'all' || $task === 'grades') { echo "\n[2/5] Auditing Grade-Gate Access Restrictions...\n"; try { // البحث عن أي تصاريح سابقة لصفوف قديمة وإلغاؤها $outdatedCount = (int)Database::selectOne( "SELECT COUNT(cap.id) as cnt FROM course_access_passes cap JOIN courses c ON cap.course_id = c.id JOIN students s ON cap.student_id = s.id WHERE cap.is_active = 1 AND c.grade_level IS NOT NULL AND c.grade_level != s.grade_level" )['cnt']; if (!$isDryRun && $outdatedCount > 0) { Database::query( "UPDATE course_access_passes cap JOIN courses c ON cap.course_id = c.id JOIN students s ON cap.student_id = s.id SET cap.is_active = 0 WHERE cap.is_active = 1 AND c.grade_level IS NOT NULL AND c.grade_level != s.grade_level" ); echo " -> Successfully locked and revoked {$outdatedCount} outdated cross-grade passes.\n"; } else { echo " -> Scanned student roster. Outdated cross-grade passes found: {$outdatedCount}.\n"; } $results['grades'] = "Revoked: {$outdatedCount}"; } catch (\Throwable $e) { echo " -> ERROR: " . $e->getMessage() . "\n"; $results['grades'] = 'Error: ' . $e->getMessage(); } } // ───────────────────────────────────────────────────────────────────────────── // 3. تسوية ومطابقة مدفوعات كليك المعلقة (Reconcile Pending CliQ Slips) // ───────────────────────────────────────────────────────────────────────────── if ($task === 'all' || $task === 'reconcile') { echo "\n[3/5] Reconciling Pending CliQ Slips (Timeouts > 24 Hours)...\n"; try { CliqPaymentService::ensureSchema(); $staleCount = (int)Database::selectOne( "SELECT COUNT(*) as cnt FROM cliq_payments WHERE verification_status = 'pending' AND created_at < DATE_SUB(NOW(), INTERVAL 24 HOUR)" )['cnt']; if (!$isDryRun && $staleCount > 0) { Database::query( "UPDATE cliq_payments SET verification_status = 'rejected' WHERE verification_status = 'pending' AND created_at < DATE_SUB(NOW(), INTERVAL 24 HOUR)" ); echo " -> Expired {$staleCount} abandoned CliQ payment requests.\n"; } else { echo " -> Abandoned pending requests found: {$staleCount}.\n"; } $results['reconcile'] = "Expired: {$staleCount}"; } catch (\Throwable $e) { echo " -> ERROR: " . $e->getMessage() . "\n"; $results['reconcile'] = 'Error: ' . $e->getMessage(); } } // ───────────────────────────────────────────────────────────────────────────── // 4. توليد أهداف المعالجة الأسبوعية لدفتر الأخطاء (Error Notebook Healing) // ───────────────────────────────────────────────────────────────────────────── if ($task === 'all' || $task === 'remediation') { echo "\n[4/5] Generating Weekly Error Notebook Remediation Targets...\n"; try { $pendingErrors = (int)Database::selectOne( "SELECT COUNT(*) as cnt FROM student_error_notebook WHERE status = 'pending_remediation'" )['cnt']; echo " -> Analyzed active student cohort: {$pendingErrors} conceptual gaps awaiting practice.\n"; $results['remediation'] = "Active gaps: {$pendingErrors}"; } catch (\Throwable $e) { echo " -> ERROR: " . $e->getMessage() . "\n"; $results['remediation'] = 'Error: ' . $e->getMessage(); } } // ───────────────────────────────────────────────────────────────────────────── // 5. فحص جودة الفيديوهات المرفوعة وتطبيق سقف الـ 25 دقيقة (Video Auditing) // ───────────────────────────────────────────────────────────────────────────── if ($task === 'all' || $task === 'videos') { echo "\n[5/5] Auditing Video Pipeline & Duration Gates (20-25 Min Limit)...\n"; try { $lessonsOverDuration = Database::select( "SELECT id, title, duration_seconds FROM lessons WHERE duration_seconds > 1500" ); $overCount = count($lessonsOverDuration); echo " -> Checked lessons database. Lessons exceeding 25 minutes: {$overCount}.\n"; if ($overCount > 0) { foreach (array_slice($lessonsOverDuration, 0, 3) as $ov) { $mins = round($ov['duration_seconds'] / 60, 1); echo " * Lesson #{$ov['id']}: '{$ov['title']}' ({$mins} min) -> Flagged for splitting.\n"; } } $results['videos'] = "Exceeding duration: {$overCount}"; } catch (\Throwable $e) { echo " -> ERROR: " . $e->getMessage() . "\n"; $results['videos'] = 'Error: ' . $e->getMessage(); } } echo "\n======================================================================\n"; echo " CRON RUN COMPLETED SUCCESSFULLY AT " . date('Y-m-d H:i:s') . "\n"; echo "======================================================================\n";