Update: 2026-06-30 23:32:14

This commit is contained in:
Hamza-Ayed
2026-06-30 23:32:15 +03:00
parent 808066f4a6
commit d2ce4bdb16
7 changed files with 277 additions and 17 deletions

View File

@@ -0,0 +1,76 @@
<?php
header('Content-Type: application/json; charset=utf-8');
require_once __DIR__ . '/../../connect.php'; // Includes db connection
$zone_name = filterRequest('zone_name');
$latitude = filterRequest('latitude');
$longitude = filterRequest('longitude');
$radius_meters = filterRequest('radius_meters');
$country_code = filterRequest('country_code');
$priority = filterRequest('priority') ?? 1;
if (empty($zone_name) || empty($latitude) || empty($longitude) || empty($radius_meters) || empty($country_code)) {
echo json_encode(["status" => "error", "message" => "Missing required fields"]);
exit;
}
try {
// 1. Check for overlapping zones
// Using Haversine formula directly in SQL to find any zone where distance < (new_radius + existing_radius)
$sql = "
SELECT id, zone_name, radius_meters,
(
6371000 * acos(
cos(radians(:new_lat)) * cos(radians(latitude)) *
cos(radians(longitude) - radians(:new_lng)) +
sin(radians(:new_lat)) * sin(radians(latitude))
)
) AS distance_meters
FROM geofence_zones
WHERE is_active = 1 AND country_code = :country_code
HAVING distance_meters < (radius_meters + :new_radius)
LIMIT 1
";
$stmt = $con->prepare($sql);
$stmt->bindValue(':new_lat', (float) $latitude);
$stmt->bindValue(':new_lng', (float) $longitude);
$stmt->bindValue(':new_radius', (int) $radius_meters, PDO::PARAM_INT);
$stmt->bindValue(':country_code', $country_code);
$stmt->execute();
$overlapping_zone = $stmt->fetch(PDO::FETCH_ASSOC);
if ($overlapping_zone) {
echo json_encode([
"status" => "error",
"message" => "Zone overlaps with existing zone: " . $overlapping_zone['zone_name'],
"overlap_details" => $overlapping_zone
]);
exit;
}
// 2. Insert new zone
$insert_sql = "INSERT INTO geofence_zones (zone_name, latitude, longitude, radius_meters, priority, country_code)
VALUES (:zone_name, :lat, :lng, :radius, :priority, :country)";
$insert_stmt = $con->prepare($insert_sql);
$insert_stmt->bindValue(':zone_name', $zone_name);
$insert_stmt->bindValue(':lat', (float) $latitude);
$insert_stmt->bindValue(':lng', (float) $longitude);
$insert_stmt->bindValue(':radius', (int) $radius_meters, PDO::PARAM_INT);
$insert_stmt->bindValue(':priority', (int) $priority, PDO::PARAM_INT);
$insert_stmt->bindValue(':country', $country_code);
$insert_stmt->execute();
echo json_encode([
"status" => "success",
"message" => "Geofence zone added successfully",
"zone_id" => $con->lastInsertId()
]);
} catch (Exception $e) {
error_log("Error adding geofence zone: " . $e->getMessage());
echo json_encode(["status" => "error", "message" => "Server error"]);
}
?>

View File

@@ -0,0 +1,29 @@
<?php
header('Content-Type: application/json; charset=utf-8');
require_once __DIR__ . '/../../connect.php';
// Optional filter: days
$days = filterRequest('days') ?? 7;
try {
$sql = "SELECT latitude, longitude, source, created_at
FROM passenger_opening_locations
WHERE created_at >= DATE_SUB(NOW(), INTERVAL :days DAY)
ORDER BY created_at DESC LIMIT 5000"; // Limit to prevent massive payloads
$stmt = $con->prepare($sql);
$stmt->bindValue(':days', (int) $days, PDO::PARAM_INT);
$stmt->execute();
$locations = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode([
"status" => "success",
"data" => $locations
]);
} catch (Exception $e) {
error_log("Error fetching heatmap data: " . $e->getMessage());
echo json_encode(["status" => "error", "message" => "Server error"]);
}
?>

View File

@@ -22,6 +22,10 @@ class LocationIntelligenceEngine {
* @return array Optional new geofence regions to register on user's device
*/
public function processLocationUpdate($passengerId, $lat, $lng, $source = 'app_usage', $batteryLevel = null) {
if (!function_exists('sendFCM_Internal')) {
require_once __DIR__ . '/../../functions.php';
}
// 1. Update Database
$this->updateLocationDatabase($passengerId, $lat, $lng, $source, $batteryLevel);
@@ -72,13 +76,7 @@ class LocationIntelligenceEngine {
$stmt->execute([':lat' => $lat, ':lng' => $lng]);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
private function evaluateCampaignOpportunity($passengerId, $zone, $source) {
// Avoid spamming if source is silent_push, maybe only do it for geofence or app_usage
if ($source === 'silent_push') {
return; // Don't trigger active campaigns on silent push to save battery and avoid weird timing
}
// 1. Check if passenger received a campaign recently (Anti-Spam)
$sqlSpamCheck = "SELECT COUNT(*) FROM marketing_campaigns_log
WHERE passenger_id = :pid
@@ -89,16 +87,37 @@ class LocationIntelligenceEngine {
$spamCount = intval($stmtSpam->fetchColumn());
if ($spamCount == 0) {
// 2. Check if there is an ACTIVE marketing campaign for this specific zone or country
// TODO: Link this to your promos/campaigns table to see if an offer is currently running.
// DO NOT send a generic "Welcome" notification as it is annoying to users.
// We only send a push if there's a real incentive (e.g. discount code).
// 2. Fetch Active Campaign (Placeholder for real campaign DB logic)
// Currently, we just send a generic welcome to the zone if priority is high.
if ($zone['priority'] >= 1) {
$this->sendCampaignNotification($passengerId, $zone);
}
}
}
private function sendCampaignNotification($passengerId, $zone) {
// Get passenger token
$sql = "SELECT users_token, country_code FROM users WHERE users_id = :pid";
$stmt = $this->db->prepare($sql);
$stmt->execute([':pid' => $passengerId]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if ($user && !empty($user['users_token'])) {
$title = "مرحباً بك في " . $zone['zone_name'] . " \u{1F389}";
$body = "اطلب رحلتك الآن من " . $zone['zone_name'] . " واستمتع بتجربة سيرو!";
// Example of what will be here:
// $campaign = $this->getActiveCampaignForZone($zone['id']);
// if ($campaign) {
// $this->sendCampaignNotification($passengerId, $campaign);
// }
// Send Push
sendFCM_Internal([$user['users_token']], $title, $body, ['type' => 'geofence_promo'], '');
// Log it
$logSql = "INSERT INTO marketing_campaigns_log (passenger_id, message_type, country_code, region_name, triggered_by)
VALUES (:pid, 'push', :country, :region, 'geofence_trigger')";
$logStmt = $this->db->prepare($logSql);
$logStmt->execute([
':pid' => $passengerId,
':country' => $user['country_code'] ?? 'JO',
':region' => $zone['zone_name']
]);
}
}