文件
mashangyoujiu/Application/Api/Controller/RiderLocationController.class.php

475 行
16 KiB
PHP

<?php
namespace Api\Controller;
use Api\Controller\BaseController;
/**
* 骑手位置API控制器
* Class RiderLocationController
* @package Api\Controller
*/
class RiderLocationController extends BaseController
{
/**
* 更新骑手位置
* @return void
*/
public function updateLocation()
{
$param = I('post.');
// 验证骑手身份
$riderInfo = $this->checkRiderAuth();
if ($riderInfo['status'] != 1) {
$this->ajaxReturn($riderInfo);
}
$riderId = $riderInfo['data']['id'];
// 验证参数
if (empty($param['lat']) || empty($param['lng'])) {
$this->ajaxReturn(callBack(statusCode()['error'], '位置信息不能为空'));
}
// 验证坐标范围
if ($param['lat'] < -90 || $param['lat'] > 90) {
$this->ajaxReturn(callBack(statusCode()['error'], '纬度范围错误'));
}
if ($param['lng'] < -180 || $param['lng'] > 180) {
$this->ajaxReturn(callBack(statusCode()['error'], '经度范围错误'));
}
$riderInfoModel = D('RiderInfo');
$riderLocationModel = D('RiderLocation');
// 更新骑手信息表中的最新位置
$updateData = [
'last_location_lat' => $param['lat'],
'last_location_lng' => $param['lng'],
'last_location_time' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
];
$result = $riderInfoModel->updateRider(['id' => $riderId], $updateData);
if ($result) {
// 记录位置轨迹
$locationData = [
'rider_id' => $riderId,
'lat' => $param['lat'],
'lng' => $param['lng'],
'accuracy' => $param['accuracy'] ?? 0, // GPS精度
'speed' => $param['speed'] ?? 0, // 速度
'bearing' => $param['bearing'] ?? 0, // 方向角
'altitude' => $param['altitude'] ?? 0, // 海拔
'record_time' => date('Y-m-d H:i:s'),
'add_time' => date('Y-m-d H:i:s')
];
$riderLocationModel->addLocation($locationData);
// 检查是否有需要更新的订单
$this->checkOrderLocationUpdate($riderId, $param['lat'], $param['lng']);
$this->ajaxReturn(callBack(statusCode()['success'], '位置更新成功'));
} else {
$this->ajaxReturn(callBack(statusCode()['error'], '位置更新失败'));
}
}
/**
* 批量更新位置
* @return void
*/
public function batchUpdateLocation()
{
$param = I('post.');
// 验证骑手身份
$riderInfo = $this->checkRiderAuth();
if ($riderInfo['status'] != 1) {
$this->ajaxReturn($riderInfo);
}
$riderId = $riderInfo['data']['id'];
if (empty($param['locations']) || !is_array($param['locations'])) {
$this->ajaxReturn(callBack(statusCode()['error'], '位置数据不能为空'));
}
$riderLocationModel = D('RiderLocation');
$locationDataList = [];
$latestLocation = null;
$latestTime = 0;
foreach ($param['locations'] as $location) {
if (empty($location['lat']) || empty($location['lng']) || empty($location['record_time'])) {
continue;
}
$recordTime = strtotime($location['record_time']);
if ($recordTime > $latestTime) {
$latestTime = $recordTime;
$latestLocation = $location;
}
$locationDataList[] = [
'rider_id' => $riderId,
'lat' => $location['lat'],
'lng' => $location['lng'],
'accuracy' => $location['accuracy'] ?? 0,
'speed' => $location['speed'] ?? 0,
'bearing' => $location['bearing'] ?? 0,
'altitude' => $location['altitude'] ?? 0,
'record_time' => $location['record_time'],
'add_time' => date('Y-m-d H:i:s')
];
}
if (empty($locationDataList)) {
$this->ajaxReturn(callBack(statusCode()['error'], '没有有效的位置数据'));
}
// 批量插入位置记录
$result = $riderLocationModel->addLocationBatch($locationDataList);
if ($result) {
// 更新最新位置到骑手信息表
if ($latestLocation) {
$riderInfoModel = D('RiderInfo');
$riderInfoModel->updateRider(['id' => $riderId], [
'last_location_lat' => $latestLocation['lat'],
'last_location_lng' => $latestLocation['lng'],
'last_location_time' => $latestLocation['record_time'],
'updated_at' => date('Y-m-d H:i:s')
]);
}
$this->ajaxReturn(callBack(statusCode()['success'], '批量位置更新成功', [
'updated_count' => count($locationDataList)
]));
} else {
$this->ajaxReturn(callBack(statusCode()['error'], '批量位置更新失败'));
}
}
/**
* 获取骑手位置轨迹
* @return void
*/
public function getLocationTrack()
{
$param = I('post.');
// 验证骑手身份
$riderInfo = $this->checkRiderAuth();
if ($riderInfo['status'] != 1) {
$this->ajaxReturn($riderInfo);
}
$riderId = $riderInfo['data']['id'];
$startTime = $param['start_time'] ?? date('Y-m-d 00:00:00');
$endTime = $param['end_time'] ?? date('Y-m-d 23:59:59');
$limit = isset($param['limit']) ? intval($param['limit']) : 100;
$riderLocationModel = D('RiderLocation');
$track = $riderLocationModel->getLocationTrack($riderId, $startTime, $endTime, $limit);
// 计算轨迹统计信息
$stats = $riderLocationModel->getRiderActivityStats($riderId, date('Y-m-d', strtotime($startTime)));
$this->ajaxReturn(callBack(statusCode()['success'], '获取位置轨迹成功', [
'track' => $track,
'stats' => $stats
]));
}
/**
* 获取附近的骑手
* @return void
*/
public function getNearbyRiders()
{
$param = I('post.');
// 验证骑手身份
$riderInfo = $this->checkRiderAuth();
if ($riderInfo['status'] != 1) {
$this->ajaxReturn($riderInfo);
}
if (empty($param['lat']) || empty($param['lng'])) {
$this->ajaxReturn(callBack(statusCode()['error'], '位置信息不能为空'));
}
$radius = isset($param['radius']) ? floatval($param['radius']) : 5; // 默认5公里
$riderLocationModel = D('RiderLocation');
// 获取附近骑手
$nearbyRiders = $riderLocationModel->getRidersInRange($param['lat'], $param['lng'], $radius);
// 获取骑手详细信息
if (!empty($nearbyRiders)) {
$riderIds = array_column($nearbyRiders, 'rider_id');
$riderInfoModel = D('RiderInfo');
$riderInfos = $riderInfoModel->getList(['id' => ['in', $riderIds]], 'id,real_name,phone,work_status,rating');
$riderInfoMap = [];
foreach ($riderInfos as $info) {
$riderInfoMap[$info['id']] = $info;
}
// 合并骑手信息
foreach ($nearbyRiders as &$rider) {
if (isset($riderInfoMap[$rider['rider_id']])) {
$rider = array_merge($rider, $riderInfoMap[$rider['rider_id']]);
}
}
}
$this->ajaxReturn(callBack(statusCode()['success'], '获取附近骑手成功', [
'riders' => $nearbyRiders,
'total' => count($nearbyRiders),
'radius' => $radius
]));
}
/**
* 获取区域骑手密度
* @return void
*/
public function getRiderDensity()
{
$param = I('post.');
if (empty($param['lat']) || empty($param['lng'])) {
$this->ajaxReturn(callBack(statusCode()['error'], '位置信息不能为空'));
}
$radius = isset($param['radius']) ? floatval($param['radius']) : 3; // 默认3公里
$timeRange = isset($param['time_range']) ? intval($param['time_range']) : 30; // 默认30分钟
$riderLocationModel = D('RiderLocation');
$density = $riderLocationModel->getRiderDensity($param['lat'], $param['lng'], $radius, $timeRange);
$this->ajaxReturn(callBack(statusCode()['success'], '获取骑手密度成功', $density));
}
/**
* 预测到达时间
* @return void
*/
public function predictArrivalTime()
{
$param = I('post.');
// 验证骑手身份
$riderInfo = $this->checkRiderAuth();
if ($riderInfo['status'] != 1) {
$this->ajaxReturn($riderInfo);
}
$riderId = $riderInfo['data']['id'];
if (empty($param['dest_lat']) || empty($param['dest_lng'])) {
$this->ajaxReturn(callBack(statusCode()['error'], '目标位置不能为空'));
}
$avgSpeed = isset($param['avg_speed']) ? floatval($param['avg_speed']) : 20; // 默认20公里/小时
$riderLocationModel = D('RiderLocation');
$prediction = $riderLocationModel->predictArrivalTime($riderId, $param['dest_lat'], $param['dest_lng'], $avgSpeed);
$this->ajaxReturn(callBack(statusCode()['success'], '预测到达时间成功', $prediction));
}
/**
* 开始位置追踪
* @return void
*/
public function startTracking()
{
$param = I('post.');
// 验证骑手身份
$riderInfo = $this->checkRiderAuth();
if ($riderInfo['status'] != 1) {
$this->ajaxReturn($riderInfo);
}
$riderId = $riderInfo['data']['id'];
$riderInfoModel = D('RiderInfo');
// 更新追踪状态
$result = $riderInfoModel->updateRider(['id' => $riderId], [
'location_tracking' => 1,
'tracking_start_time' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
]);
if ($result) {
$this->ajaxReturn(callBack(statusCode()['success'], '开始位置追踪成功'));
} else {
$this->ajaxReturn(callBack(statusCode()['error'], '开始位置追踪失败'));
}
}
/**
* 停止位置追踪
* @return void
*/
public function stopTracking()
{
$param = I('post.');
// 验证骑手身份
$riderInfo = $this->checkRiderAuth();
if ($riderInfo['status'] != 1) {
$this->ajaxReturn($riderInfo);
}
$riderId = $riderInfo['data']['id'];
$riderInfoModel = D('RiderInfo');
// 更新追踪状态
$result = $riderInfoModel->updateRider(['id' => $riderId], [
'location_tracking' => 0,
'tracking_end_time' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
]);
if ($result) {
$this->ajaxReturn(callBack(statusCode()['success'], '停止位置追踪成功'));
} else {
$this->ajaxReturn(callBack(statusCode()['error'], '停止位置追踪失败'));
}
}
/**
* 验证骑手身份
* @return array
*/
private function checkRiderAuth()
{
$token = I('post.token', '');
if (empty($token)) {
return callBack(statusCode()['error'], '请先登录');
}
$riderTokenModel = D('RiderToken');
$tokenInfo = $riderTokenModel->getFind(['token' => $token]);
if (empty($tokenInfo)) {
return callBack(statusCode()['error'], '登录已过期,请重新登录');
}
// 检查token是否过期
if (strtotime($tokenInfo['expire_time']) < time()) {
return callBack(statusCode()['error'], '登录已过期,请重新登录');
}
// 获取骑手信息
$riderInfoModel = D('RiderInfo');
$riderInfo = $riderInfoModel->getFind(['id' => $tokenInfo['rider_id']]);
if (empty($riderInfo)) {
return callBack(statusCode()['error'], '骑手信息不存在');
}
if ($riderInfo['status'] != 1) {
return callBack(statusCode()['error'], '账号已被禁用');
}
return callBack(statusCode()['success'], '验证成功', $riderInfo);
}
/**
* 检查订单位置更新
* @param int $riderId 骑手ID
* @param float $lat 纬度
* @param float $lng 经度
* @return void
*/
private function checkOrderLocationUpdate($riderId, $lat, $lng)
{
// 获取骑手当前进行中的订单(统一集合)
import('@.Common.Business.OrderStatusUtil');
$inProgress = \Common\Business\OrderStatusUtil::getInProgressStatuses();
$riderOrderModel = D('RiderOrder');
$activeOrders = $riderOrderModel->getList([
'rider_id' => $riderId,
'order_status' => ['in', implode(',', $inProgress)]
]);
if (empty($activeOrders)) {
return;
}
$riderLocationModel = D('RiderLocation');
foreach ($activeOrders as $order) {
// 检查是否到达门店
if ($order['order_status'] == 1 && !empty($order['store_lat']) && !empty($order['store_lng'])) {
$storeDistance = $riderLocationModel->calculateDistance($lat, $lng, $order['store_lat'], $order['store_lng']);
// 如果距离门店小于100米,自动更新为已取货状态
if ($storeDistance <= 0.1) {
$riderOrderModel->updateOrder(['id' => $order['id']], [
'order_status' => 2,
'pickup_time' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s')
]);
// 同步状态
$orderStatusSync = new \Common\Business\OrderStatusSync();
$orderStatusSync->syncOrderStatus([
'rider_order_id' => $order['id'],
'bill_id' => $order['bill_id'],
'action' => 'pickup',
'extra_data' => []
]);
}
}
// 检查是否到达客户地址
if ($order['order_status'] == 3 && !empty($order['customer_lat']) && !empty($order['customer_lng'])) {
$customerDistance = $riderLocationModel->calculateDistance($lat, $lng, $order['customer_lat'], $order['customer_lng']);
// 如果距离客户小于50米,发送到达提醒
if ($customerDistance <= 0.05) {
// 这里可以发送推送通知给客户
$this->sendArrivalNotification($order['bill_id'], $riderId);
}
}
}
}
/**
* 发送到达通知
* @param int $billId 订单ID
* @param int $riderId 骑手ID
* @return void
*/
private function sendArrivalNotification($billId, $riderId)
{
// 这里可以实现推送通知功能
// 暂时只记录日志
$orderSyncLogModel = D('OrderSyncLog');
$orderSyncLogModel->addLog([
'rider_order_id' => 0,
'bill_id' => $billId,
'action' => 'arrival_notification',
'description' => "骑手{$riderId}即将到达",
'sync_time' => date('Y-m-d H:i:s'),
'add_time' => date('Y-m-d H:i:s')
]);
}
}