文件
mashangyoujiu/Application/Common/Model/RiderLocationModel.class.php

340 行
9.7 KiB
PHP

<?php
namespace Common\Model;
use Common\Model\BaseModel;
/**
* 骑手位置模型
* Class RiderLocationModel
* @package Common\Model
*/
class RiderLocationModel extends BaseModel
{
/**
* 表名
* @var string
*/
protected $tableName = 'rider_location';
/**
* 添加位置记录
* @param array $data
* @return int|false
*/
public function addLocation($data)
{
return $this->add($data);
}
/**
* 批量添加位置记录
* @param array $dataList
* @return int|false
*/
public function addLocationBatch($dataList)
{
return $this->addAll($dataList);
}
/**
* 获取骑手最新位置
* @param int $riderId 骑手ID
* @return array
*/
public function getLatestLocation($riderId)
{
return $this->where(['rider_id' => $riderId])
->order('add_time DESC')
->find();
}
/**
* 获取骑手位置轨迹
* @param int $riderId 骑手ID
* @param string $startTime 开始时间
* @param string $endTime 结束时间
* @param int $limit 限制数量
* @return array
*/
public function getLocationTrack($riderId, $startTime = '', $endTime = '', $limit = 100)
{
$where = ['rider_id' => $riderId];
if (!empty($startTime)) {
$where['add_time'][] = ['egt', $startTime];
}
if (!empty($endTime)) {
$where['add_time'][] = ['elt', $endTime];
}
return $this->where($where)
->order('add_time ASC')
->limit($limit)
->select();
}
/**
* 获取指定范围内的骑手
* @param float $centerLat 中心纬度
* @param float $centerLng 中心经度
* @param float $radius 半径(公里)
* @param array $riderIds 指定骑手ID列表(可选)
* @return array
*/
public function getRidersInRange($centerLat, $centerLng, $radius, $riderIds = [])
{
// 计算经纬度范围
$latRange = $radius / 111; // 1度纬度约等于111公里
$lngRange = $radius / (111 * cos(deg2rad($centerLat))); // 经度范围需要考虑纬度
$minLat = $centerLat - $latRange;
$maxLat = $centerLat + $latRange;
$minLng = $centerLng - $lngRange;
$maxLng = $centerLng + $lngRange;
$where = [
'lat' => ['between', [$minLat, $maxLat]],
'lng' => ['between', [$minLng, $maxLng]]
];
if (!empty($riderIds)) {
$where['rider_id'] = ['in', $riderIds];
}
// 获取每个骑手的最新位置
$subQuery = $this->field('rider_id, MAX(add_time) as max_time')
->where($where)
->group('rider_id')
->buildSql();
$locations = $this->alias('rl1')
->join("({$subQuery}) rl2 ON rl1.rider_id = rl2.rider_id AND rl1.add_time = rl2.max_time")
->field('rl1.*')
->select();
// 精确计算距离并过滤
$result = [];
foreach ($locations as $location) {
$distance = $this->calculateDistance($centerLat, $centerLng, $location['lat'], $location['lng']);
if ($distance <= $radius) {
$location['distance'] = $distance;
$result[] = $location;
}
}
// 按距离排序
usort($result, function($a, $b) {
return $a['distance'] <=> $b['distance'];
});
return $result;
}
/**
* 计算两点间距离
* @param float $lat1 纬度1
* @param float $lng1 经度1
* @param float $lat2 纬度2
* @param float $lng2 经度2
* @return float 距离(公里)
*/
public function calculateDistance($lat1, $lng1, $lat2, $lng2)
{
if (empty($lat1) || empty($lng1) || empty($lat2) || empty($lng2)) {
return 999;
}
$earthRadius = 6371; // 地球半径,单位:公里
$lat1Rad = deg2rad($lat1);
$lng1Rad = deg2rad($lng1);
$lat2Rad = deg2rad($lat2);
$lng2Rad = deg2rad($lng2);
$deltaLat = $lat2Rad - $lat1Rad;
$deltaLng = $lng2Rad - $lng1Rad;
$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));
return $earthRadius * $c;
}
/**
* 清理过期位置记录
* @param int $days 保留天数
* @return int
*/
public function cleanExpiredLocations($days = 7)
{
$expireDate = date('Y-m-d H:i:s', strtotime("-{$days} days"));
// 删除过期的位置记录
return $this->where(['add_time' => ['lt', $expireDate]])->delete();
}
/**
* 获取骑手活动统计
* @param int $riderId 骑手ID
* @param string $date 日期
* @return array
*/
public function getRiderActivityStats($riderId, $date = '')
{
if (empty($date)) {
$date = date('Y-m-d');
}
$startTime = $date . ' 00:00:00';
$endTime = $date . ' 23:59:59';
$where = [
'rider_id' => $riderId,
'add_time' => ['between', [$startTime, $endTime]]
];
$locations = $this->where($where)
->order('add_time ASC')
->select();
if (empty($locations)) {
return [
'total_distance' => 0,
'active_time' => 0,
'location_count' => 0,
'first_location_time' => '',
'last_location_time' => ''
];
}
// 计算总距离
$totalDistance = 0;
for ($i = 1; $i < count($locations); $i++) {
$distance = $this->calculateDistance(
$locations[$i-1]['lat'],
$locations[$i-1]['lng'],
$locations[$i]['lat'],
$locations[$i]['lng']
);
$totalDistance += $distance;
}
// 计算活动时间
$firstTime = strtotime($locations[0]['add_time']);
$lastTime = strtotime($locations[count($locations)-1]['add_time']);
$activeTime = $lastTime - $firstTime;
return [
'total_distance' => round($totalDistance, 2),
'active_time' => $activeTime,
'location_count' => count($locations),
'first_location_time' => $locations[0]['add_time'],
'last_location_time' => $locations[count($locations)-1]['add_time']
];
}
/**
* 获取区域内骑手密度
* @param float $centerLat 中心纬度
* @param float $centerLng 中心经度
* @param float $radius 半径(公里)
* @param int $timeRange 时间范围(分钟)
* @return array
*/
public function getRiderDensity($centerLat, $centerLng, $radius, $timeRange = 30)
{
$startTime = date('Y-m-d H:i:s', strtotime("-{$timeRange} minutes"));
// 计算经纬度范围
$latRange = $radius / 111;
$lngRange = $radius / (111 * cos(deg2rad($centerLat)));
$minLat = $centerLat - $latRange;
$maxLat = $centerLat + $latRange;
$minLng = $centerLng - $lngRange;
$maxLng = $centerLng + $lngRange;
$where = [
'lat' => ['between', [$minLat, $maxLat]],
'lng' => ['between', [$minLng, $maxLng]],
'add_time' => ['egt', $startTime]
];
// 获取活跃骑手数量
$activeRiders = $this->where($where)
->field('rider_id')
->group('rider_id')
->select();
$riderCount = count($activeRiders);
$area = pi() * $radius * $radius; // 圆形区域面积
$density = $area > 0 ? $riderCount / $area : 0;
return [
'rider_count' => $riderCount,
'area' => round($area, 2),
'density' => round($density, 4),
'radius' => $radius,
'time_range' => $timeRange
];
}
/**
* 预测骑手到达时间
* @param int $riderId 骑手ID
* @param float $destLat 目标纬度
* @param float $destLng 目标经度
* @param float $avgSpeed 平均速度(公里/小时)
* @return array
*/
public function predictArrivalTime($riderId, $destLat, $destLng, $avgSpeed = 20)
{
// 获取骑手最新位置
$latestLocation = $this->getLatestLocation($riderId);
if (empty($latestLocation)) {
return [
'estimated_time' => 0,
'distance' => 0,
'error' => '无法获取骑手位置'
];
}
// 计算距离
$distance = $this->calculateDistance(
$latestLocation['lat'],
$latestLocation['lng'],
$destLat,
$destLng
);
// 计算预计时间(分钟)
$estimatedTime = ($distance / $avgSpeed) * 60;
// 考虑交通因素,增加20%的时间
$estimatedTime *= 1.2;
return [
'estimated_time' => round($estimatedTime, 0),
'distance' => round($distance, 2),
'avg_speed' => $avgSpeed,
'current_location' => [
'lat' => $latestLocation['lat'],
'lng' => $latestLocation['lng'],
'record_time' => $latestLocation['add_time']
]
];
}
/**
* 删除位置记录
* @param array $where 删除条件
* @return int
*/
public function deleteLocation($where)
{
return $this->where($where)->delete();
}
}