文件
mashangyoujiu/Application/Retail/Controller/RiderStatsController.class.php

582 行
21 KiB
PHP

<?php
namespace Retail\Controller;
use Think\Controller;
use Exception;
/**
* 配送统计控制器
*/
class RiderStatsController extends Controller {
/**
* 配送统计首页
*/
public function index() {
$this->display();
}
/**
* 获取概览统计数据
*/
public function getOverviewStats() {
$timeRange = I('timeRange', 'today', 'trim');
$startDate = I('startDate', '', 'trim');
$endDate = I('endDate', '', 'trim');
try {
$dateCondition = $this->getDateCondition($timeRange, $startDate, $endDate);
$prevDateCondition = $this->getPrevDateCondition($timeRange, $startDate, $endDate);
$orderModel = M('rider_order');
// 当前期间统计
$currentStats = $this->calculatePeriodStats($orderModel, $dateCondition);
// 上一期间统计(用于计算变化率)
$prevStats = $this->calculatePeriodStats($orderModel, $prevDateCondition);
// 计算变化率
$orderChange = $this->calculateChangeRate($currentStats['totalOrders'], $prevStats['totalOrders']);
$revenueChange = $this->calculateChangeRate($currentStats['totalRevenue'], $prevStats['totalRevenue']);
$timeChange = $this->calculateChangeRate($currentStats['avgDeliveryTime'], $prevStats['avgDeliveryTime'], true);
$completionChange = $this->calculateChangeRate($currentStats['completionRate'], $prevStats['completionRate']);
$result = array(
'totalOrders' => $currentStats['totalOrders'],
'orderChange' => $orderChange,
'totalRevenue' => $currentStats['totalRevenue'],
'revenueChange' => $revenueChange,
'avgDeliveryTime' => $currentStats['avgDeliveryTime'],
'timeChange' => $timeChange,
'completionRate' => $currentStats['completionRate'],
'completionChange' => $completionChange
);
$this->ajaxReturn(array(
'code' => 200,
'msg' => '获取成功',
'data' => $result
));
} catch (Exception $e) {
$this->ajaxReturn(array(
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
));
}
}
/**
* 获取订单趋势数据
*/
public function getOrderTrend() {
$timeRange = I('timeRange', 'week', 'trim');
$startDate = I('startDate', '', 'trim');
$endDate = I('endDate', '', 'trim');
try {
$dateCondition = $this->getDateCondition($timeRange, $startDate, $endDate);
$orderModel = M('rider_order');
$trendData = array();
$labels = array();
if ($timeRange === 'week') {
// 最近7天的数据
for ($i = 6; $i >= 0; $i--) {
$date = date('Y-m-d', strtotime("-{$i} days"));
$count = $orderModel->where(array('add_time' => array('like', $date . '%')))->count();
$trendData[] = intval($count);
$labels[] = date('m-d', strtotime($date));
}
} elseif ($timeRange === 'month') {
// 最近30天,按周统计
for ($i = 3; $i >= 0; $i--) {
$startWeek = date('Y-m-d', strtotime("-" . (($i + 1) * 7) . " days"));
$endWeek = date('Y-m-d', strtotime("-" . ($i * 7) . " days"));
$count = $orderModel->where(array(
'add_time' => array('between', array($startWeek, $endWeek))
))->count();
$trendData[] = intval($count);
$labels[] = date('m-d', strtotime($startWeek)) . '~' . date('m-d', strtotime($endWeek));
}
} else {
// 自定义时间范围,按天统计
$start = strtotime($startDate);
$end = strtotime($endDate);
$days = ($end - $start) / (24 * 3600) + 1;
for ($i = 0; $i < $days; $i++) {
$date = date('Y-m-d', $start + $i * 24 * 3600);
$count = $orderModel->where(array('add_time' => array('like', $date . '%')))->count();
$trendData[] = intval($count);
$labels[] = date('m-d', strtotime($date));
}
}
$this->ajaxReturn(array(
'code' => 200,
'msg' => '获取成功',
'data' => array(
'labels' => $labels,
'data' => $trendData
)
));
} catch (Exception $e) {
$this->ajaxReturn(array(
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
));
}
}
/**
* 获取收入趋势数据
*/
public function getRevenueTrend() {
$timeRange = I('timeRange', 'week', 'trim');
$startDate = I('startDate', '', 'trim');
$endDate = I('endDate', '', 'trim');
try {
$dateCondition = $this->getDateCondition($timeRange, $startDate, $endDate);
$orderModel = M('rider_order');
$trendData = array();
$labels = array();
if ($timeRange === 'week') {
// 最近7天的数据
for ($i = 6; $i >= 0; $i--) {
$date = date('Y-m-d', strtotime("-{$i} days"));
$revenue = $orderModel
->where(array(
'order_status' => 6,
'delivery_time' => array('like', $date . '%')
))
->sum('delivery_fee');
$trendData[] = floatval($revenue);
$labels[] = date('m-d', strtotime($date));
}
} elseif ($timeRange === 'month') {
// 最近30天,按周统计
for ($i = 3; $i >= 0; $i--) {
$startWeek = date('Y-m-d', strtotime("-" . (($i + 1) * 7) . " days"));
$endWeek = date('Y-m-d', strtotime("-" . ($i * 7) . " days"));
$revenue = $orderModel
->where(array(
'order_status' => 6,
'delivery_time' => array('between', array($startWeek, $endWeek))
))
->sum('delivery_fee');
$trendData[] = floatval($revenue);
$labels[] = date('m-d', strtotime($startWeek)) . '~' . date('m-d', strtotime($endWeek));
}
}
$this->ajaxReturn(array(
'code' => 200,
'msg' => '获取成功',
'data' => array(
'labels' => $labels,
'data' => $trendData
)
));
} catch (Exception $e) {
$this->ajaxReturn(array(
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
));
}
}
/**
* 获取订单状态分布
*/
public function getOrderStatusDistribution() {
$timeRange = I('timeRange', 'today', 'trim');
$startDate = I('startDate', '', 'trim');
$endDate = I('endDate', '', 'trim');
try {
$dateCondition = $this->getDateCondition($timeRange, $startDate, $endDate);
$orderModel = M('rider_order');
import('Common.Business.OrderStatusUtil');
$statusList = array('pending', 'accepted', 'picking_up', 'delivering', 'completed', 'cancelled');
$statusData = array();
foreach ($statusList as $status) {
$condition = $dateCondition;
$cond = \Common\Business\OrderStatusUtil::getStatusCondition($status);
if (!is_null($cond)) {
$condition['order_status'] = $cond;
}
$count = $orderModel->where($condition)->count();
$statusData[] = array(
'name' => \Common\Business\OrderStatusUtil::getStatusText($status),
'value' => intval($count)
);
}
$this->ajaxReturn(array(
'code' => 200,
'msg' => '获取成功',
'data' => $statusData
));
} catch (Exception $e) {
$this->ajaxReturn(array(
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
));
}
}
/**
* 获取骑手排行榜
*/
public function getRiderRanking() {
$timeRange = I('timeRange', 'today', 'trim');
$startDate = I('startDate', '', 'trim');
$endDate = I('endDate', '', 'trim');
$limit = I('limit', 10, 'intval');
try {
$dateCondition = $this->getDateCondition($timeRange, $startDate, $endDate);
$sql = "SELECT
r.id, r.rider_name AS name, r.rider_phone AS phone, r.rider_avatar AS avatar, r.rating,
COUNT(o.id) as total_orders,
SUM(o.delivery_fee) as total_revenue,
AVG(TIMESTAMPDIFF(MINUTE, o.accept_time, o.delivery_time)) as avg_delivery_time,
SUM(o.distance) as total_distance,
(COUNT(CASE WHEN o.order_status = 6 THEN 1 END) * 100.0 / COUNT(o.id)) as completion_rate
FROM ls_rider_info r
LEFT JOIN ls_rider_order o ON r.id = o.rider_id";
if (!empty($dateCondition)) {
$sql .= " AND " . $this->buildWhereString($this->aliasConditions($dateCondition, 'o'));
}
$sql .= " GROUP BY r.id
HAVING total_orders > 0
ORDER BY total_orders DESC
LIMIT {$limit}";
$riderStats = M()->query($sql);
// 处理数据
foreach ($riderStats as &$rider) {
$rider['total_orders'] = intval($rider['total_orders']);
$rider['total_revenue'] = floatval($rider['total_revenue']);
$rider['avg_delivery_time'] = intval($rider['avg_delivery_time']);
$rider['total_distance'] = floatval($rider['total_distance']);
$rider['completion_rate'] = floatval($rider['completion_rate']);
$rider['rating'] = floatval($rider['rating']);
$rider['avatar'] = $rider['avatar'] ?: '';
}
$this->ajaxReturn(array(
'code' => 200,
'msg' => '获取成功',
'data' => $riderStats ?: array()
));
} catch (Exception $e) {
$this->ajaxReturn(array(
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
));
}
}
/**
* 获取配送时段分析
*/
public function getTimeSlotAnalysis() {
$timeRange = I('timeRange', 'today', 'trim');
$startDate = I('startDate', '', 'trim');
$endDate = I('endDate', '', 'trim');
try {
$dateCondition = $this->getDateCondition($timeRange, $startDate, $endDate);
$orderModel = M('rider_order');
$timeSlots = array(
'8-10' => array(8, 10),
'10-12' => array(10, 12),
'12-14' => array(12, 14),
'14-16' => array(14, 16),
'16-18' => array(16, 18),
'18-20' => array(18, 20),
'20-22' => array(20, 22)
);
$slotData = array();
$labels = array();
foreach ($timeSlots as $label => $hours) {
$condition = $dateCondition;
$condition['_string'] = "HOUR(add_time) >= {$hours[0]} AND HOUR(add_time) < {$hours[1]}";
$count = $orderModel->where($condition)->count();
$slotData[] = intval($count);
$labels[] = $label;
}
$this->ajaxReturn(array(
'code' => 200,
'msg' => '获取成功',
'data' => array(
'labels' => $labels,
'data' => $slotData
)
));
} catch (Exception $e) {
$this->ajaxReturn(array(
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
));
}
}
/**
* 获取骑手详细统计
*/
public function getRiderDetailStats() {
$timeRange = I('timeRange', 'today', 'trim');
$startDate = I('startDate', '', 'trim');
$endDate = I('endDate', '', 'trim');
$page = I('page', 1, 'intval');
$pageSize = I('pageSize', 20, 'intval');
try {
$dateCondition = $this->getDateCondition($timeRange, $startDate, $endDate);
$sql = "SELECT
r.id, r.rider_name AS name, r.rider_phone AS phone, r.rider_avatar AS avatar, r.rating,
COUNT(o.id) as total_orders,
SUM(o.delivery_fee) as total_revenue,
AVG(TIMESTAMPDIFF(MINUTE, o.accept_time, o.delivery_time)) as avg_delivery_time,
SUM(o.distance) as total_distance,
(COUNT(CASE WHEN o.order_status = 6 THEN 1 END) * 100.0 / COUNT(o.id)) as completion_rate,
0 as online_hours
FROM ls_rider_info r
LEFT JOIN ls_rider_order o ON r.id = o.rider_id";
if (!empty($dateCondition)) {
$sql .= " AND " . $this->buildWhereString($this->aliasConditions($dateCondition, 'o'));
}
$sql .= " GROUP BY r.id
ORDER BY total_orders DESC";
// 获取总数
$countSql = "SELECT COUNT(*) as total FROM ({$sql}) as temp";
$totalResult = M()->query($countSql);
$totalCount = $totalResult[0]['total'];
// 分页
$offset = ($page - 1) * $pageSize;
$sql .= " LIMIT {$offset}, {$pageSize}";
$riderStats = M()->query($sql);
// 处理数据
foreach ($riderStats as &$rider) {
$rider['total_orders'] = intval($rider['total_orders']);
$rider['total_revenue'] = floatval($rider['total_revenue']);
$rider['avg_delivery_time'] = intval($rider['avg_delivery_time']);
$rider['total_distance'] = floatval($rider['total_distance']);
$rider['completion_rate'] = floatval($rider['completion_rate']);
$rider['rating'] = floatval($rider['rating']);
$rider['online_hours'] = intval($rider['online_hours']);
$rider['avatar'] = $rider['avatar'] ?: '';
}
$this->ajaxReturn(array(
'code' => 200,
'msg' => '获取成功',
'data' => array(
'list' => $riderStats ?: array(),
'totalCount' => $totalCount,
'currentPage' => $page,
'pageSize' => $pageSize
)
));
} catch (Exception $e) {
$this->ajaxReturn(array(
'code' => 500,
'msg' => '获取失败:' . $e->getMessage()
));
}
}
/**
* 根据时间范围获取日期条件
*/
private function getDateCondition($timeRange, $startDate = '', $endDate = '') {
$condition = array();
switch ($timeRange) {
case 'today':
$condition['add_time'] = array('like', date('Y-m-d') . '%');
break;
case 'yesterday':
$condition['add_time'] = array('like', date('Y-m-d', strtotime('-1 day')) . '%');
break;
case 'week':
$condition['add_time'] = array('egt', date('Y-m-d', strtotime('-7 days')));
break;
case 'month':
$condition['add_time'] = array('egt', date('Y-m-d', strtotime('-30 days')));
break;
case 'custom':
if (!empty($startDate) && !empty($endDate)) {
$condition['add_time'] = array('between', array($startDate, $endDate));
}
break;
}
return $condition;
}
/**
* 获取上一期间的日期条件
*/
private function getPrevDateCondition($timeRange, $startDate = '', $endDate = '') {
$condition = array();
switch ($timeRange) {
case 'today':
$condition['add_time'] = array('like', date('Y-m-d', strtotime('-1 day')) . '%');
break;
case 'yesterday':
$condition['add_time'] = array('like', date('Y-m-d', strtotime('-2 days')) . '%');
break;
case 'week':
$condition['add_time'] = array('between', array(
date('Y-m-d', strtotime('-14 days')),
date('Y-m-d', strtotime('-7 days'))
));
break;
case 'month':
$condition['add_time'] = array('between', array(
date('Y-m-d', strtotime('-60 days')),
date('Y-m-d', strtotime('-30 days'))
));
break;
}
return $condition;
}
/**
* 计算期间统计数据
*/
private function calculatePeriodStats($orderModel, $dateCondition) {
// 总订单数
$totalOrders = $orderModel->where($dateCondition)->count();
// 总收入
$revenueCondition = array_merge($dateCondition, array('order_status' => 6));
$totalRevenue = $orderModel->where($revenueCondition)->sum('delivery_fee');
// 平均配送时间
$avgDeliveryTime = $orderModel
->where($revenueCondition)
->avg('TIMESTAMPDIFF(MINUTE, accept_time, delivery_time)');
// 完成率
$completedOrders = $orderModel->where($revenueCondition)->count();
$completionRate = $totalOrders > 0 ? ($completedOrders / $totalOrders) * 100 : 0;
return array(
'totalOrders' => intval($totalOrders),
'totalRevenue' => floatval($totalRevenue),
'avgDeliveryTime' => intval($avgDeliveryTime),
'completionRate' => floatval($completionRate)
);
}
/**
* 计算变化率
*/
private function calculateChangeRate($current, $previous, $reverse = false) {
if ($previous == 0) {
return $current > 0 ? 100 : 0;
}
$rate = (($current - $previous) / $previous) * 100;
// 对于时间类指标,减少是好的
if ($reverse) {
$rate = -$rate;
}
return round($rate, 1);
}
/**
* 构建WHERE字符串
*/
private function buildWhereString($conditions) {
$whereArray = array();
foreach ($conditions as $field => $value) {
if (is_array($value)) {
if ($value[0] === 'like') {
$whereArray[] = "{$field} LIKE '{$value[1]}'";
} elseif ($value[0] === 'between') {
$whereArray[] = "{$field} BETWEEN '{$value[1][0]}' AND '{$value[1][1]}'";
} elseif ($value[0] === 'egt') {
$whereArray[] = "{$field} >= '{$value[1]}'";
}
} else {
$whereArray[] = "{$field} = '{$value}'";
}
}
return implode(' AND ', $whereArray);
}
/**
* 为条件键添加表别名前缀
*/
private function aliasConditions($conditions, $alias) {
$result = array();
foreach ($conditions as $field => $value) {
$prefixed = $alias . '.' . $field;
$result[$prefixed] = $value;
}
return $result;
}
/**
* 获取状态文本
*/
private function getStatusText($status) {
$statusTexts = array(
'pending' => '待分配',
'accepted' => '已接单',
'picking_up' => '取货中',
'delivering' => '配送中',
'completed' => '已完成',
'cancelled' => '已取消'
);
return isset($statusTexts[$status]) ? $statusTexts[$status] : '未知';
}
}
?>