init backend presensi

This commit is contained in:
mwpn
2026-03-05 14:37:36 +07:00
commit b4fda6b9c9
319 changed files with 27261 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Modules\Geo\Entities;
use CodeIgniter\Entity\Entity;
/**
* Zone Entity
*
* Represents a geographic zone/geofence in the system.
*/
class Zone extends Entity
{
/**
* Attributes that can be mass assigned
*
* @var array<string>
*/
protected $allowedFields = [
'zone_code',
'zone_name',
'latitude',
'longitude',
'radius_meters',
'is_active',
];
/**
* Attributes that should be cast to specific types
*
* @var array<string, string>
*/
protected $casts = [
'id' => 'integer',
'latitude' => 'float',
'longitude' => 'float',
'radius_meters' => 'integer',
'is_active' => 'boolean',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
/**
* Check if zone is active
*
* @return bool
*/
public function isActive(): bool
{
return (bool) $this->attributes['is_active'];
}
}

View File

@@ -0,0 +1,95 @@
<?php
namespace App\Modules\Geo\Models;
use App\Modules\Geo\Entities\Zone;
use CodeIgniter\Model;
/**
* Zone Model
*
* Handles database operations for zones.
*/
class ZoneModel extends Model
{
protected $table = 'zones';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $returnType = Zone::class;
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = [
'zone_code',
'zone_name',
'latitude',
'longitude',
'radius_meters',
'is_active',
];
// Dates
protected $useTimestamps = true;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [
'zone_code' => 'required|max_length[100]|is_unique[zones.zone_code,id,{id}]',
'zone_name' => 'required|max_length[255]',
'latitude' => 'required|decimal',
'longitude' => 'required|decimal',
'radius_meters' => 'required|integer|greater_than[0]',
'is_active' => 'permit_empty|in_list[0,1]',
];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
/**
* Find zone by zone_code
*
* @param string $zoneCode
* @return Zone|null
*/
public function findByZoneCode(string $zoneCode): ?Zone
{
return $this->where('zone_code', $zoneCode)->first();
}
/**
* Find active zone by zone_code
*
* @param string $zoneCode
* @return Zone|null
*/
public function findActiveByZoneCode(string $zoneCode): ?Zone
{
return $this->where('zone_code', $zoneCode)
->where('is_active', 1)
->first();
}
/**
* Get all active zones
*
* @return array
*/
public function findAllActive(): array
{
return $this->where('is_active', 1)->findAll();
}
}

View File

@@ -0,0 +1,12 @@
<?php
/**
* Geo Module Routes
*
* This file is automatically loaded by ModuleLoader.
* Define your geo/zone management routes here.
*
* @var RouteCollection $routes
*/
// Geo routes will be defined here

View File

@@ -0,0 +1,96 @@
<?php
namespace App\Modules\Geo\Services;
/**
* Geo Fence Service
*
* Handles geofencing logic using Haversine formula for distance calculation.
*/
class GeoFenceService
{
/**
* Earth's radius in meters
*/
private const EARTH_RADIUS_METERS = 6371000;
/**
* Check if a point (latitude, longitude) is inside a zone
*
* Uses Haversine formula to calculate distance between two points
* on Earth's surface and compares it to zone radius.
*
* @param float $lat Latitude of the point to check
* @param float $lng Longitude of the point to check
* @param array $zone Zone data with 'latitude', 'longitude', and 'radius_meters'
* @return bool True if point is inside zone, false otherwise
*/
public function isInsideZone(float $lat, float $lng, array $zone): bool
{
// Validate zone data
if (!isset($zone['latitude']) || !isset($zone['longitude']) || !isset($zone['radius_meters'])) {
return false;
}
$zoneLat = (float) $zone['latitude'];
$zoneLng = (float) $zone['longitude'];
$radiusMeters = (int) $zone['radius_meters'];
// Calculate distance using Haversine formula
$distance = $this->calculateHaversineDistance($lat, $lng, $zoneLat, $zoneLng);
// Check if distance is within radius
return $distance <= $radiusMeters;
}
/**
* Calculate distance between two points using Haversine formula
*
* Haversine formula calculates the great-circle distance between
* two points on a sphere given their longitudes and latitudes.
*
* @param float $lat1 Latitude of first point
* @param float $lng1 Longitude of first point
* @param float $lat2 Latitude of second point
* @param float $lng2 Longitude of second point
* @return float Distance in meters
*/
private function calculateHaversineDistance(float $lat1, float $lng1, float $lat2, float $lng2): float
{
// Convert degrees to radians
$lat1Rad = deg2rad($lat1);
$lng1Rad = deg2rad($lng1);
$lat2Rad = deg2rad($lat2);
$lng2Rad = deg2rad($lng2);
// Calculate differences
$deltaLat = $lat2Rad - $lat1Rad;
$deltaLng = $lng2Rad - $lng1Rad;
// Haversine formula
$a = sin($deltaLat / 2) * sin($deltaLat / 2) +
cos($lat1Rad) * cos($lat2Rad) *
sin($deltaLng / 2) * sin($deltaLng / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
// Distance in meters
$distance = self::EARTH_RADIUS_METERS * $c;
return $distance;
}
/**
* Calculate distance between two points (public helper method)
*
* @param float $lat1 Latitude of first point
* @param float $lng1 Longitude of first point
* @param float $lat2 Latitude of second point
* @param float $lng2 Longitude of second point
* @return float Distance in meters
*/
public function calculateDistance(float $lat1, float $lng1, float $lat2, float $lng2): float
{
return $this->calculateHaversineDistance($lat1, $lng1, $lat2, $lng2);
}
}