prepare(" SELECT a.id, a.action, a.entity_type, a.entity_id, a.new_data, a.created_at, u.name AS user_name FROM audit_logs a LEFT JOIN users u ON a.user_id = u.id $where ORDER BY a.created_at DESC LIMIT 20 "); $stmt->execute($params); $activities = $stmt->fetchAll(); foreach ($activities as &$activity) { $activity['user_name'] = decryptIfEncrypted($activity['user_name'] ?? null) ?: 'مستخدم مجهول'; $activity['details'] = decodeAuditData($activity['new_data'] ?? null); $activity['summary'] = buildActivitySummary($activity); unset($activity['new_data']); } unset($activity); json_success($activities); } catch (\Exception $e) { error_log('Dashboard Recent Activity Error: ' . $e->getMessage()); json_error('Failed to fetch recent activity', 500); } function decodeAuditData(?string $json): array { if (!$json) { return []; } $decoded = json_decode($json, true); if (!is_array($decoded)) { return []; } return decryptAuditPayload($decoded); } function decryptAuditPayload(array $payload): array { foreach ($payload as $key => $value) { if (is_array($value)) { $payload[$key] = decryptAuditPayload($value); continue; } if (is_string($value)) { $payload[$key] = decryptIfEncrypted($value); } } return $payload; } function decryptIfEncrypted(mixed $value): string { if ($value === null) { return ''; } $text = trim((string)$value); if ($text === '' || !looksEncrypted($text)) { return $text; } try { $decrypted = Encryption::decrypt($text); return $decrypted !== false ? $decrypted : $text; } catch (\Throwable $e) { return $text; } } function looksEncrypted(string $value): bool { $normalized = str_starts_with($value, '==') ? substr($value, 2) : $value; if (strlen($normalized) < 40 || strlen($normalized) % 4 !== 0) { return false; } return (bool)preg_match('/^[A-Za-z0-9+\/]+={0,2}$/', $normalized); } function buildActivitySummary(array $activity): string { $data = is_array($activity['details'] ?? null) ? $activity['details'] : []; $action = (string)($activity['action'] ?? ''); return match ($action) { 'payment.created' => buildPaymentSummary('تم إنشاء طلب دفع', $data), 'payment.approved' => buildPaymentSummary('تم اعتماد طلب دفع', $data), 'payment.rejected' => buildPaymentSummary('تم رفض طلب دفع', $data), 'subscription.activated' => buildPaymentSummary('تم تفعيل الاشتراك', $data), 'invoice.approved' => buildEntitySummary('تم اعتماد الفاتورة', $data, ['invoice_number', 'number']), 'invoice.extracted' => buildEntitySummary('تم استخراج بيانات الفاتورة', $data, ['invoice_number', 'number']), 'company.created' => buildEntitySummary('تمت إضافة شركة', $data, ['name', 'company_name']), 'user.created' => buildEntitySummary('تمت إضافة مستخدم', $data, ['name', 'email']), default => '', }; } function buildPaymentSummary(string $label, array $data): string { $parts = [$label]; $plan = firstTextValue($data, ['plan_name', 'plan_name_ar', 'plan_id']); if ($plan !== '') { $parts[] = "الباقة: {$plan}"; } $amount = firstTextValue($data, ['amount', 'amount_jod', 'price_jod']); if ($amount !== '') { $parts[] = "القيمة: {$amount} د.أ"; } $reference = firstTextValue($data, ['ref', 'reference', 'internal_reference']); if ($reference !== '') { $parts[] = "المرجع: {$reference}"; } return implode(' - ', $parts); } function buildEntitySummary(string $label, array $data, array $keys): string { $value = firstTextValue($data, $keys); return $value === '' ? $label : "{$label}: {$value}"; } function firstTextValue(array $data, array $keys): string { foreach ($keys as $key) { if (!array_key_exists($key, $data) || $data[$key] === null) { continue; } $value = $data[$key]; if (is_scalar($value)) { $text = trim((string)$value); if ($text !== '') { return $text; } } } return ''; }