display(); } /** * 获取在线骑手列表 */ public function getOnlineRiders() { $page = I('page', 1, 'intval'); $pageSize = I('pageSize', 20, 'intval'); $keyword = I('keyword', '', 'trim'); $area_id = I('area_id', 0, 'intval'); $work_status = I('work_status', '', 'trim'); $where = array('u.user_type' => 'rider', 'u.status' => 1); // 关键词搜索 if (!empty($keyword)) { $where['_string'] = "(u.nickname LIKE '%{$keyword}%' OR u.phone LIKE '%{$keyword}%')"; } // 工作状态筛选 if ($work_status !== '') { $where['rs.work_status'] = $work_status; } // 区域筛选 if ($area_id > 0) { $where['ra.area_id'] = $area_id; } $model = M('users'); $total = $model->alias('u') ->join('ls_rider_status rs ON u.id = rs.rider_id', 'LEFT') ->join('ls_rider_area ra ON u.id = ra.rider_id', 'LEFT') ->where($where) ->count('DISTINCT u.id'); $list = $model->alias('u') ->field('u.*, rs.work_status, rs.current_lat, rs.current_lng, rs.last_update_time, rs.order_count_today') ->join('ls_rider_status rs ON u.id = rs.rider_id', 'LEFT') ->join('ls_rider_area ra ON u.id = ra.rider_id', 'LEFT') ->where($where) ->group('u.id') ->order('rs.last_update_time desc') ->page($page, $pageSize) ->select(); // 格式化数据 foreach ($list as &$item) { $item['work_status_text'] = $this->getWorkStatusText($item['work_status']); $item['last_update_time_format'] = $item['last_update_time'] ? date('Y-m-d H:i:s', $item['last_update_time']) : ''; // 计算在线时长 if ($item['work_status'] == 'online') { $online_duration = time() - strtotime(date('Y-m-d 00:00:00')); $item['online_duration'] = $this->formatDuration($online_duration); } else { $item['online_duration'] = '0分钟'; } // 获取当前订单信息 $current_order = M('order')->where(array( 'rider_id' => $item['id'], 'status' => array('in', array('accepted', 'picked_up', 'delivering')) ))->find(); $item['current_order'] = $current_order; // 获取配送区域 $areas = M('rider_area')->alias('ra') ->field('da.area_name') ->join('ls_delivery_area da ON ra.area_id = da.id') ->where(array('ra.rider_id' => $item['id'])) ->select(); $item['areas'] = $areas; } $this->ajaxReturn(array( 'code' => 0, 'msg' => 'success', 'data' => array( 'list' => $list, 'total' => $total ) )); } /** * 获取骑手实时位置 */ public function getRiderLocation() { $rider_id = I('rider_id', 0, 'intval'); if (empty($rider_id)) { $this->ajaxReturn(array('code' => 1, 'msg' => '参数错误')); } $location = M('rider_status')->where(array('rider_id' => $rider_id))->find(); if (empty($location)) { $this->ajaxReturn(array('code' => 1, 'msg' => '未找到位置信息')); } // 获取骑手信息 $rider = M('users')->where(array('id' => $rider_id))->find(); $data = array( 'rider_id' => $rider_id, 'nickname' => $rider['nickname'], 'phone' => $rider['phone'], 'current_lat' => floatval($location['current_lat']), 'current_lng' => floatval($location['current_lng']), 'work_status' => $location['work_status'], 'work_status_text' => $this->getWorkStatusText($location['work_status']), 'last_update_time' => date('Y-m-d H:i:s', $location['last_update_time']) ); // 获取最近的位置轨迹 $tracks = M('rider_location_track')->where(array( 'rider_id' => $rider_id, 'create_time' => array('egt', time() - 3600) // 最近1小时 ))->order('create_time desc')->limit(50)->select(); $track_points = array(); foreach ($tracks as $track) { $track_points[] = array( 'lat' => floatval($track['latitude']), 'lng' => floatval($track['longitude']), 'time' => date('H:i:s', $track['create_time']) ); } $data['track_points'] = $track_points; $this->ajaxReturn(array( 'code' => 0, 'msg' => 'success', 'data' => $data )); } /** * 获取骑手工作统计 */ public function getRiderWorkStats() { $rider_id = I('rider_id', 0, 'intval'); $date = I('date', date('Y-m-d'), 'trim'); if (empty($rider_id)) { $this->ajaxReturn(array('code' => 1, 'msg' => '参数错误')); } $start_time = strtotime($date . ' 00:00:00'); $end_time = strtotime($date . ' 23:59:59'); // 基础统计 $stats = array(); // 订单统计 $order_where = array( 'rider_id' => $rider_id, 'create_time' => array('between', array($start_time, $end_time)) ); $stats['order_count'] = M('order')->where($order_where)->count(); $stats['completed_count'] = M('order')->where(array_merge($order_where, array('status' => 'completed')))->count(); $stats['cancelled_count'] = M('order')->where(array_merge($order_where, array('status' => 'cancelled')))->count(); // 收入统计 $stats['total_income'] = M('order')->where(array_merge($order_where, array('status' => 'completed')))->sum('delivery_fee') ?: 0; $stats['total_income_format'] = number_format($stats['total_income'], 2); // 在线时长统计 $online_logs = M('rider_online_log')->where(array( 'rider_id' => $rider_id, 'date' => $date ))->find(); $stats['online_duration'] = $online_logs ? $online_logs['online_duration'] : 0; $stats['online_duration_format'] = $this->formatDuration($stats['online_duration']); // 平均配送时间 $avg_delivery_time = M('order')->where(array_merge($order_where, array('status' => 'completed'))) ->avg('delivery_duration'); $stats['avg_delivery_time'] = $avg_delivery_time ? round($avg_delivery_time / 60, 1) : 0; // 准时率 $timeout_count = M('order')->where(array_merge($order_where, array( 'status' => 'completed', 'is_timeout' => 1 )))->count(); $stats['punctuality_rate'] = $stats['completed_count'] > 0 ? round((($stats['completed_count'] - $timeout_count) / $stats['completed_count']) * 100, 2) : 0; $this->ajaxReturn(array( 'code' => 0, 'msg' => 'success', 'data' => $stats )); } /** * 获取实时监控数据 */ public function getRealtimeData() { $area_id = I('area_id', 0, 'intval'); $where = array('u.user_type' => 'rider', 'u.status' => 1); if ($area_id > 0) { $where['ra.area_id'] = $area_id; } // 在线骑手统计 $online_count = M('users')->alias('u') ->join('ls_rider_status rs ON u.id = rs.rider_id', 'LEFT') ->join('ls_rider_area ra ON u.id = ra.rider_id', 'LEFT') ->where(array_merge($where, array('rs.work_status' => 'online'))) ->count('DISTINCT u.id'); // 忙碌骑手统计 $busy_count = M('users')->alias('u') ->join('ls_rider_status rs ON u.id = rs.rider_id', 'LEFT') ->join('ls_rider_area ra ON u.id = ra.rider_id', 'LEFT') ->where(array_merge($where, array('rs.work_status' => 'busy'))) ->count('DISTINCT u.id'); // 离线骑手统计 $offline_count = M('users')->alias('u') ->join('ls_rider_status rs ON u.id = rs.rider_id', 'LEFT') ->join('ls_rider_area ra ON u.id = ra.rider_id', 'LEFT') ->where(array_merge($where, array('rs.work_status' => array('in', array('offline', ''))))) ->count('DISTINCT u.id'); // 今日订单统计 $today_start = strtotime(date('Y-m-d 00:00:00')); $today_end = strtotime(date('Y-m-d 23:59:59')); $order_where = array('create_time' => array('between', array($today_start, $today_end))); if ($area_id > 0) { $order_where['delivery_area_id'] = $area_id; } $today_orders = M('order')->where($order_where)->count(); $completed_orders = M('order')->where(array_merge($order_where, array('status' => 'completed')))->count(); $pending_orders = M('order')->where(array_merge($order_where, array('status' => 'pending')))->count(); $data = array( 'rider_stats' => array( 'online_count' => $online_count, 'busy_count' => $busy_count, 'offline_count' => $offline_count, 'total_count' => $online_count + $busy_count + $offline_count ), 'order_stats' => array( 'today_orders' => $today_orders, 'completed_orders' => $completed_orders, 'pending_orders' => $pending_orders, 'completion_rate' => $today_orders > 0 ? round(($completed_orders / $today_orders) * 100, 2) : 0 ) ); $this->ajaxReturn(array( 'code' => 0, 'msg' => 'success', 'data' => $data )); } /** * 获取骑手分布地图数据 */ public function getRiderMapData() { $area_id = I('area_id', 0, 'intval'); $where = array( 'u.user_type' => 'rider', 'u.status' => 1, 'rs.work_status' => array('in', array('online', 'busy')) ); if ($area_id > 0) { $where['ra.area_id'] = $area_id; } $riders = M('users')->alias('u') ->field('u.id, u.nickname, u.phone, rs.current_lat, rs.current_lng, rs.work_status') ->join('ls_rider_status rs ON u.id = rs.rider_id') ->join('ls_rider_area ra ON u.id = ra.rider_id', 'LEFT') ->where($where) ->group('u.id') ->select(); $map_data = array(); foreach ($riders as $rider) { if (empty($rider['current_lat']) || empty($rider['current_lng'])) { continue; } $map_data[] = array( 'rider_id' => $rider['id'], 'nickname' => $rider['nickname'], 'phone' => $rider['phone'], 'lat' => floatval($rider['current_lat']), 'lng' => floatval($rider['current_lng']), 'work_status' => $rider['work_status'], 'work_status_text' => $this->getWorkStatusText($rider['work_status']) ); } $this->ajaxReturn(array( 'code' => 0, 'msg' => 'success', 'data' => $map_data )); } /** * 强制下线骑手 */ public function forceOffline() { $rider_id = I('rider_id', 0, 'intval'); $reason = I('reason', '', 'trim'); if (empty($rider_id)) { $this->ajaxReturn(array('code' => 1, 'msg' => '参数错误')); } if (empty($reason)) { $this->ajaxReturn(array('code' => 1, 'msg' => '请填写下线原因')); } // 检查骑手是否有进行中的订单 $active_order = M('order')->where(array( 'rider_id' => $rider_id, 'status' => array('in', array('accepted', 'picked_up', 'delivering')) ))->find(); if ($active_order) { $this->ajaxReturn(array('code' => 1, 'msg' => '该骑手有进行中的订单,无法强制下线')); } // 更新骑手状态 $result = M('rider_status')->where(array('rider_id' => $rider_id))->save(array( 'work_status' => 'offline', 'last_update_time' => time() )); if ($result !== false) { // 记录操作日志 M('rider_operation_log')->add(array( 'rider_id' => $rider_id, 'operator_id' => session('admin_id'), 'action' => 'force_offline', 'reason' => $reason, 'create_time' => time() )); $this->ajaxReturn(array('code' => 0, 'msg' => '操作成功')); } else { $this->ajaxReturn(array('code' => 1, 'msg' => '操作失败')); } } /** * 发送通知给骑手 */ public function sendNotification() { $rider_id = I('rider_id', 0, 'intval'); $message = I('message', '', 'trim'); if (empty($rider_id)) { $this->ajaxReturn(array('code' => 1, 'msg' => '参数错误')); } if (empty($message)) { $this->ajaxReturn(array('code' => 1, 'msg' => '请输入通知内容')); } // 这里可以集成推送服务 // 示例:调用推送接口发送通知 // 记录通知日志 M('rider_notification_log')->add(array( 'rider_id' => $rider_id, 'sender_id' => session('admin_id'), 'message' => $message, 'create_time' => time() )); $this->ajaxReturn(array('code' => 0, 'msg' => '通知发送成功')); } /** * 导出监控数据 */ public function exportMonitorData() { $date = I('date', date('Y-m-d'), 'trim'); $area_id = I('area_id', 0, 'intval'); $start_time = strtotime($date . ' 00:00:00'); $end_time = strtotime($date . ' 23:59:59'); $where = array('u.user_type' => 'rider', 'u.status' => 1); if ($area_id > 0) { $where['ra.area_id'] = $area_id; } $list = M('users')->alias('u') ->field('u.*, rs.work_status, rs.last_update_time, rs.order_count_today') ->join('ls_rider_status rs ON u.id = rs.rider_id', 'LEFT') ->join('ls_rider_area ra ON u.id = ra.rider_id', 'LEFT') ->where($where) ->group('u.id') ->select(); // 导出Excel vendor('PHPExcel.PHPExcel'); $objPHPExcel = new \PHPExcel(); $objPHPExcel->setActiveSheetIndex(0); $objActSheet = $objPHPExcel->getActiveSheet(); // 设置表头 $objActSheet->setCellValue('A1', '骑手ID'); $objActSheet->setCellValue('B1', '姓名'); $objActSheet->setCellValue('C1', '手机号'); $objActSheet->setCellValue('D1', '工作状态'); $objActSheet->setCellValue('E1', '今日订单数'); $objActSheet->setCellValue('F1', '今日收入'); $objActSheet->setCellValue('G1', '在线时长'); $objActSheet->setCellValue('H1', '最后更新时间'); // 填充数据 $row = 2; foreach ($list as $item) { // 计算今日收入 $today_income = M('order')->where(array( 'rider_id' => $item['id'], 'status' => 'completed', 'create_time' => array('between', array($start_time, $end_time)) ))->sum('delivery_fee') ?: 0; // 获取在线时长 $online_log = M('rider_online_log')->where(array( 'rider_id' => $item['id'], 'date' => $date ))->find(); $online_duration = $online_log ? $this->formatDuration($online_log['online_duration']) : '0分钟'; $objActSheet->setCellValue('A' . $row, $item['id']); $objActSheet->setCellValue('B' . $row, $item['nickname']); $objActSheet->setCellValue('C' . $row, $item['phone']); $objActSheet->setCellValue('D' . $row, $this->getWorkStatusText($item['work_status'])); $objActSheet->setCellValue('E' . $row, $item['order_count_today'] ?: 0); $objActSheet->setCellValue('F' . $row, number_format($today_income, 2)); $objActSheet->setCellValue('G' . $row, $online_duration); $objActSheet->setCellValue('H' . $row, $item['last_update_time'] ? date('Y-m-d H:i:s', $item['last_update_time']) : ''); $row++; } $filename = '骑手监控数据_' . $date . '_' . date('His') . '.xlsx'; header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); header('Content-Disposition: attachment;filename="' . $filename . '"'); header('Cache-Control: max-age=0'); $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007'); $objWriter->save('php://output'); exit; } /** * 格式化时长 */ private function formatDuration($seconds) { $hours = floor($seconds / 3600); $minutes = floor(($seconds % 3600) / 60); if ($hours > 0) { return $hours . '小时' . $minutes . '分钟'; } else { return $minutes . '分钟'; } } /** * 获取工作状态文本 */ private function getWorkStatusText($status) { $statusMap = array( 'online' => '在线空闲', 'busy' => '配送中', 'offline' => '离线', 'rest' => '休息中' ); return isset($statusMap[$status]) ? $statusMap[$status] : '未知'; } } ?>