561 行
20 KiB
PHP
561 行
20 KiB
PHP
<?php
|
|
namespace Admin\Controller;
|
|
|
|
use Admin\Controller\BaseController;
|
|
use Common\Business\RiderOrderDispatch;
|
|
|
|
/**
|
|
* 后台骑手管理控制器
|
|
* Class RiderManageController
|
|
* @package Admin\Controller
|
|
*/
|
|
class RiderManageController extends BaseController
|
|
{
|
|
/**
|
|
* 订单分配页面
|
|
* @return void
|
|
*/
|
|
public function orderDispatch()
|
|
{
|
|
$this->display();
|
|
}
|
|
|
|
/**
|
|
* 获取待分配订单列表
|
|
* @return void
|
|
*/
|
|
public function getPendingOrders()
|
|
{
|
|
$param = I('post.');
|
|
|
|
$page = isset($param['page']) ? intval($param['page']) : 1;
|
|
$pageSize = isset($param['page_size']) ? intval($param['page_size']) : 20;
|
|
$storeId = isset($param['store_id']) ? intval($param['store_id']) : 0;
|
|
|
|
$billOrderModel = D('RetailBillOrder');
|
|
$billOnlineModel = D('RetailBillOnline');
|
|
|
|
// 构建查询条件
|
|
$where = [
|
|
'bo.type' => 1, // 商城订单
|
|
'bo.pay_state' => 1, // 已支付
|
|
'bol.pickup_type' => 2, // 配送订单
|
|
'bol.status' => 1, // 未发货
|
|
];
|
|
|
|
if ($storeId > 0) {
|
|
$where['bo.store_id'] = $storeId;
|
|
}
|
|
|
|
// 排除已分配给骑手的订单
|
|
$riderOrderModel = D('RiderOrder');
|
|
$assignedOrderIds = $riderOrderModel->getColumn(['order_status' => ['neq', -1]], 'bill_id');
|
|
if (!empty($assignedOrderIds)) {
|
|
$where['bo.id'] = ['not in', $assignedOrderIds];
|
|
}
|
|
|
|
// 获取订单列表
|
|
$orders = $billOrderModel->alias('bo')
|
|
->join('ls_retail_bill_online bol ON bo.id = bol.bill_id')
|
|
->join('ls_retail_store rs ON bo.store_id = rs.id')
|
|
->where($where)
|
|
->field('bo.*, bol.pickup_type, bol.status, bol.user_name, bol.user_phone, bol.user_address, rs.store_name, rs.lat as store_lat, rs.lng as store_lng')
|
|
->order('bo.add_time DESC')
|
|
->page($page, $pageSize)
|
|
->select();
|
|
|
|
$total = $billOrderModel->alias('bo')
|
|
->join('ls_retail_bill_online bol ON bo.id = bol.bill_id')
|
|
->where($where)
|
|
->count();
|
|
|
|
$result = [
|
|
'list' => $orders ?: [],
|
|
'total' => $total,
|
|
'page' => $page,
|
|
'page_size' => $pageSize
|
|
];
|
|
|
|
$this->ajaxReturn(callBack(statusCode()['success'], '获取待分配订单成功', $result));
|
|
}
|
|
|
|
/**
|
|
* 手动分配订单
|
|
* @return void
|
|
*/
|
|
public function manualDispatch()
|
|
{
|
|
$param = I('post.');
|
|
|
|
if (empty($param['bill_id'])) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '订单ID不能为空'));
|
|
}
|
|
|
|
if (empty($param['rider_id'])) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '请选择骑手'));
|
|
}
|
|
|
|
// 获取订单信息
|
|
$billOrderModel = D('RetailBillOrder');
|
|
$orderInfo = $billOrderModel->getFind(['id' => $param['bill_id']]);
|
|
|
|
if (empty($orderInfo)) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '订单不存在'));
|
|
}
|
|
|
|
$riderOrderDispatch = new RiderOrderDispatch();
|
|
|
|
$dispatchParam = [
|
|
'bill_id' => $param['bill_id'],
|
|
'merchant_id' => $orderInfo['merchant_id'],
|
|
'store_id' => $orderInfo['store_id'],
|
|
'auto_assign' => false,
|
|
'rider_id' => $param['rider_id']
|
|
];
|
|
|
|
$result = $riderOrderDispatch->dispatchOrder($dispatchParam);
|
|
|
|
$this->ajaxReturn($result);
|
|
}
|
|
|
|
/**
|
|
* 自动分配订单
|
|
* @return void
|
|
*/
|
|
public function autoDispatch()
|
|
{
|
|
$param = I('post.');
|
|
|
|
if (empty($param['bill_id'])) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '订单ID不能为空'));
|
|
}
|
|
|
|
// 获取订单信息
|
|
$billOrderModel = D('RetailBillOrder');
|
|
$orderInfo = $billOrderModel->getFind(['id' => $param['bill_id']]);
|
|
|
|
if (empty($orderInfo)) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '订单不存在'));
|
|
}
|
|
|
|
$riderOrderDispatch = new RiderOrderDispatch();
|
|
|
|
$dispatchParam = [
|
|
'bill_id' => $param['bill_id'],
|
|
'merchant_id' => $orderInfo['merchant_id'],
|
|
'store_id' => $orderInfo['store_id'],
|
|
'auto_assign' => true
|
|
];
|
|
|
|
$result = $riderOrderDispatch->dispatchOrder($dispatchParam);
|
|
|
|
$this->ajaxReturn($result);
|
|
}
|
|
|
|
/**
|
|
* 获取可用骑手列表
|
|
* @return void
|
|
*/
|
|
public function getAvailableRiders()
|
|
{
|
|
$param = I('post.');
|
|
|
|
if (empty($param['bill_id'])) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '订单ID不能为空'));
|
|
}
|
|
|
|
// 获取订单信息
|
|
$billOrderModel = D('RetailBillOrder');
|
|
$orderInfo = $billOrderModel->getFind(['id' => $param['bill_id']]);
|
|
|
|
if (empty($orderInfo)) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '订单不存在'));
|
|
}
|
|
|
|
$riderOrderDispatch = new RiderOrderDispatch();
|
|
|
|
$dispatchParam = [
|
|
'bill_id' => $param['bill_id'],
|
|
'merchant_id' => $orderInfo['merchant_id'],
|
|
'store_id' => $orderInfo['store_id'],
|
|
'auto_assign' => false
|
|
];
|
|
|
|
$result = $riderOrderDispatch->dispatchOrder($dispatchParam);
|
|
|
|
$this->ajaxReturn($result);
|
|
}
|
|
|
|
/**
|
|
* 骑手订单管理页面
|
|
* @return void
|
|
*/
|
|
public function riderOrders()
|
|
{
|
|
$this->display();
|
|
}
|
|
|
|
/**
|
|
* 获取骑手订单列表
|
|
* @return void
|
|
*/
|
|
public function getRiderOrderList()
|
|
{
|
|
$param = I('post.');
|
|
|
|
$page = isset($param['page']) ? intval($param['page']) : 1;
|
|
$pageSize = isset($param['page_size']) ? intval($param['page_size']) : 20;
|
|
$riderId = isset($param['rider_id']) ? intval($param['rider_id']) : 0;
|
|
$orderStatus = isset($param['order_status']) ? $param['order_status'] : '';
|
|
$storeId = isset($param['store_id']) ? intval($param['store_id']) : 0;
|
|
|
|
$riderOrderModel = D('RiderOrder');
|
|
|
|
// 构建查询条件
|
|
$where = [];
|
|
if ($riderId > 0) {
|
|
$where['ro.rider_id'] = $riderId;
|
|
}
|
|
if ($orderStatus !== '') {
|
|
$where['ro.order_status'] = $orderStatus;
|
|
}
|
|
if ($storeId > 0) {
|
|
$where['ro.store_id'] = $storeId;
|
|
}
|
|
|
|
// 获取订单列表
|
|
$orders = $riderOrderModel->alias('ro')
|
|
->join('ls_rider_info ri ON ro.rider_id = ri.id')
|
|
->join('ls_retail_store rs ON ro.store_id = rs.id')
|
|
->where($where)
|
|
->field('ro.*, ri.real_name as rider_name, ri.phone as rider_phone, rs.store_name')
|
|
->order('ro.add_time DESC')
|
|
->page($page, $pageSize)
|
|
->select();
|
|
|
|
$total = $riderOrderModel->alias('ro')->where($where)->count();
|
|
|
|
// 处理订单状态显示
|
|
import('Common.Business.OrderStatusUtil');
|
|
$statusMap = [
|
|
'0' => \Common\Business\OrderStatusUtil::getStatusText('pending'),
|
|
'1' => \Common\Business\OrderStatusUtil::getStatusText('accepted'),
|
|
'2' => \Common\Business\OrderStatusUtil::getStatusText('picking_up'),
|
|
'3' => \Common\Business\OrderStatusUtil::getStatusText('delivering'),
|
|
'6' => \Common\Business\OrderStatusUtil::getStatusText('completed'),
|
|
'8' => \Common\Business\OrderStatusUtil::getStatusText('cancelled'),
|
|
'7' => \Common\Business\OrderStatusUtil::getStatusText('exception')
|
|
];
|
|
|
|
foreach ($orders as &$order) {
|
|
$order['status_text'] = isset($statusMap[$order['order_status']]) ? $statusMap[$order['order_status']] : '未知';
|
|
|
|
// 添加配送凭证信息
|
|
if ($order['order_status'] == 6) { // 已送达订单
|
|
$order['has_receipt'] = !empty($order['delivery_receipt_type']);
|
|
$order['receipt_type'] = $order['delivery_receipt_type'];
|
|
$order['receipt_upload_time'] = $order['receipt_upload_time'];
|
|
}
|
|
}
|
|
|
|
$result = [
|
|
'list' => $orders ?: [],
|
|
'total' => $total,
|
|
'page' => $page,
|
|
'page_size' => $pageSize
|
|
];
|
|
|
|
$this->ajaxReturn(callBack(statusCode()['success'], '获取骑手订单列表成功', $result));
|
|
}
|
|
|
|
/**
|
|
* 获取骑手列表
|
|
* @return void
|
|
*/
|
|
public function getRiderList()
|
|
{
|
|
$param = I('post.');
|
|
|
|
$storeId = isset($param['store_id']) ? intval($param['store_id']) : 0;
|
|
$workStatus = isset($param['work_status']) ? intval($param['work_status']) : -1;
|
|
|
|
$riderInfoModel = D('RiderInfo');
|
|
|
|
// 构建查询条件
|
|
$where = [
|
|
'auth_status' => 2, // 已认证
|
|
'status' => 1 // 正常状态
|
|
];
|
|
|
|
if ($storeId > 0) {
|
|
$where['store_id'] = $storeId;
|
|
}
|
|
|
|
if ($workStatus >= 0) {
|
|
$where['work_status'] = $workStatus;
|
|
}
|
|
|
|
$riders = $riderInfoModel->getList($where, 'id,real_name,phone,work_status,rating,on_time_rate,max_orders_per_time');
|
|
|
|
// 获取每个骑手的当前订单数量
|
|
$riderOrderModel = D('RiderOrder');
|
|
foreach ($riders as &$rider) {
|
|
import('@.Common.Business.OrderStatusUtil');
|
|
$inProgress = \Common\Business\OrderStatusUtil::getInProgressStatuses();
|
|
$rider['current_orders'] = $riderOrderModel->getCount([
|
|
'rider_id' => $rider['id'],
|
|
'order_status' => ['in', implode(',', $inProgress)]
|
|
]);
|
|
$rider['work_status_text'] = $rider['work_status'] == 1 ? '在线' : '离线';
|
|
}
|
|
|
|
$this->ajaxReturn(callBack(statusCode()['success'], '获取骑手列表成功', $riders));
|
|
}
|
|
|
|
/**
|
|
* 批量分配订单
|
|
* @return void
|
|
*/
|
|
public function batchDispatch()
|
|
{
|
|
$param = I('post.');
|
|
|
|
if (empty($param['bill_ids']) || !is_array($param['bill_ids'])) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '请选择要分配的订单'));
|
|
}
|
|
|
|
$riderOrderDispatch = new RiderOrderDispatch();
|
|
$successCount = 0;
|
|
$failCount = 0;
|
|
$errors = [];
|
|
|
|
foreach ($param['bill_ids'] as $billId) {
|
|
// 获取订单信息
|
|
$billOrderModel = D('RetailBillOrder');
|
|
$orderInfo = $billOrderModel->getFind(['id' => $billId]);
|
|
|
|
if (empty($orderInfo)) {
|
|
$failCount++;
|
|
$errors[] = "订单ID {$billId} 不存在";
|
|
continue;
|
|
}
|
|
|
|
$dispatchParam = [
|
|
'bill_id' => $billId,
|
|
'merchant_id' => $orderInfo['merchant_id'],
|
|
'store_id' => $orderInfo['store_id'],
|
|
'auto_assign' => true
|
|
];
|
|
|
|
$result = $riderOrderDispatch->dispatchOrder($dispatchParam);
|
|
|
|
if ($result['status'] == 1) {
|
|
$successCount++;
|
|
} else {
|
|
$failCount++;
|
|
$errors[] = "订单ID {$billId}: " . $result['msg'];
|
|
}
|
|
}
|
|
|
|
$message = "批量分配完成,成功:{$successCount}个,失败:{$failCount}个";
|
|
|
|
$this->ajaxReturn(callBack(statusCode()['success'], $message, [
|
|
'success_count' => $successCount,
|
|
'fail_count' => $failCount,
|
|
'errors' => $errors
|
|
]));
|
|
}
|
|
|
|
/**
|
|
* 获取订单配送凭证详情
|
|
* @return void
|
|
*/
|
|
public function getOrderReceiptDetail()
|
|
{
|
|
$param = I('post.');
|
|
|
|
if (empty($param['order_id'])) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '订单ID不能为空'));
|
|
}
|
|
|
|
$riderOrderModel = D('RiderOrder');
|
|
|
|
// 获取订单配送凭证信息
|
|
$order = $riderOrderModel->alias('ro')
|
|
->join('ls_rider_info ri ON ro.rider_id = ri.id')
|
|
->join('ls_retail_store rs ON ro.store_id = rs.id')
|
|
->where(['ro.id' => $param['order_id']])
|
|
->field('ro.id, ro.order_status, ro.delivery_receipt_type, ro.delivery_receipt_photos,
|
|
ro.delivery_signature, ro.delivery_receipt_code, ro.delivery_receipt_remark,
|
|
ro.receipt_upload_time, ro.delivery_time, ro.customer_name, ro.customer_phone,
|
|
ro.customer_address, ri.real_name as rider_name, ri.phone as rider_phone,
|
|
rs.store_name')
|
|
->find();
|
|
|
|
if (empty($order)) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '订单不存在'));
|
|
}
|
|
|
|
if ($order['order_status'] != 6) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '订单未完成配送'));
|
|
}
|
|
|
|
// 格式化配送凭证数据
|
|
$receiptData = [
|
|
'order_id' => $order['id'],
|
|
'order_status' => $order['order_status'],
|
|
'delivery_time' => $order['delivery_time'],
|
|
'receipt_type' => $order['delivery_receipt_type'],
|
|
'receipt_type_text' => $this->getReceiptTypeText($order['delivery_receipt_type']),
|
|
'receipt_photos' => $order['delivery_receipt_photos'] ? explode(',', $order['delivery_receipt_photos']) : [],
|
|
'delivery_signature' => $order['delivery_signature'],
|
|
'receipt_code' => $order['delivery_receipt_code'],
|
|
'receipt_remark' => $order['delivery_receipt_remark'],
|
|
'upload_time' => $order['receipt_upload_time'],
|
|
'customer_info' => [
|
|
'name' => $order['customer_name'],
|
|
'phone' => $order['customer_phone'],
|
|
'address' => $order['customer_address']
|
|
],
|
|
'rider_info' => [
|
|
'name' => $order['rider_name'],
|
|
'phone' => $order['rider_phone']
|
|
],
|
|
'store_name' => $order['store_name']
|
|
];
|
|
|
|
$this->ajaxReturn(callBack(statusCode()['success'], '获取配送凭证成功', $receiptData));
|
|
}
|
|
|
|
/**
|
|
* 获取凭证类型文本
|
|
* @param int $type
|
|
* @return string
|
|
*/
|
|
private function getReceiptTypeText($type)
|
|
{
|
|
$typeMap = [
|
|
1 => '照片凭证',
|
|
2 => '签名凭证',
|
|
3 => '验证码凭证'
|
|
];
|
|
|
|
return isset($typeMap[$type]) ? $typeMap[$type] : '未知类型';
|
|
}
|
|
|
|
/**
|
|
* 订单重新分配
|
|
* @return void
|
|
*/
|
|
public function reassignOrder()
|
|
{
|
|
$param = I('post.');
|
|
|
|
if (empty($param['rider_order_id'])) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '骑手订单ID不能为空'));
|
|
}
|
|
|
|
$riderOrderModel = D('RiderOrder');
|
|
|
|
// 获取骑手订单信息
|
|
$riderOrder = $riderOrderModel->getFind(['id' => $param['rider_order_id']]);
|
|
if (empty($riderOrder)) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '骑手订单不存在'));
|
|
}
|
|
|
|
// 检查订单状态
|
|
if ($riderOrder['order_status'] > 1) {
|
|
$this->ajaxReturn(callBack(statusCode()['error'], '订单已开始配送,无法重新分配'));
|
|
}
|
|
|
|
// 取消当前分配
|
|
$riderOrderModel->updateOrder(['id' => $param['rider_order_id']], [
|
|
'order_status' => -2, // 已取消
|
|
'cancel_time' => date('Y-m-d H:i:s'),
|
|
'cancel_reason' => '管理员重新分配',
|
|
'updated_at' => date('Y-m-d H:i:s')
|
|
]);
|
|
|
|
// 重新分配
|
|
$riderOrderDispatch = new RiderOrderDispatch();
|
|
|
|
$dispatchParam = [
|
|
'bill_id' => $riderOrder['bill_id'],
|
|
'merchant_id' => $riderOrder['merchant_id'],
|
|
'store_id' => $riderOrder['store_id'],
|
|
'auto_assign' => true
|
|
];
|
|
|
|
$result = $riderOrderDispatch->dispatchOrder($dispatchParam);
|
|
|
|
$this->ajaxReturn($result);
|
|
}
|
|
|
|
/**
|
|
* 获取订单统计数据
|
|
* @return void
|
|
*/
|
|
public function getOrderStats()
|
|
{
|
|
$param = I('post.');
|
|
|
|
$storeId = isset($param['store_id']) ? intval($param['store_id']) : 0;
|
|
$dateRange = isset($param['date_range']) ? $param['date_range'] : 'today';
|
|
|
|
// 计算日期范围
|
|
switch ($dateRange) {
|
|
case 'today':
|
|
$startDate = date('Y-m-d 00:00:00');
|
|
$endDate = date('Y-m-d 23:59:59');
|
|
break;
|
|
case 'yesterday':
|
|
$startDate = date('Y-m-d 00:00:00', strtotime('-1 day'));
|
|
$endDate = date('Y-m-d 23:59:59', strtotime('-1 day'));
|
|
break;
|
|
case 'week':
|
|
$startDate = date('Y-m-d 00:00:00', strtotime('-7 days'));
|
|
$endDate = date('Y-m-d 23:59:59');
|
|
break;
|
|
case 'month':
|
|
$startDate = date('Y-m-01 00:00:00');
|
|
$endDate = date('Y-m-d 23:59:59');
|
|
break;
|
|
default:
|
|
$startDate = date('Y-m-d 00:00:00');
|
|
$endDate = date('Y-m-d 23:59:59');
|
|
}
|
|
|
|
$riderOrderModel = D('RiderOrder');
|
|
|
|
// 构建基础查询条件
|
|
$baseWhere = [
|
|
'add_time' => ['between', [$startDate, $endDate]]
|
|
];
|
|
|
|
if ($storeId > 0) {
|
|
$baseWhere['store_id'] = $storeId;
|
|
}
|
|
|
|
// 统计各状态订单数量
|
|
$stats = [
|
|
'total_orders' => $riderOrderModel->getCount($baseWhere),
|
|
'pending_orders' => $riderOrderModel->getCount(array_merge($baseWhere, ['order_status' => 0])),
|
|
'accepted_orders' => $riderOrderModel->getCount(array_merge($baseWhere, ['order_status' => 1])),
|
|
'delivering_orders' => $riderOrderModel->getCount(array_merge($baseWhere, ['order_status' => ['in', \Common\Business\OrderStatusUtil::getInTransitStatuses()]])),
|
|
'completed_orders' => $riderOrderModel->getCount(array_merge($baseWhere, ['order_status' => 6])),
|
|
'exception_orders' => $riderOrderModel->getCount(array_merge($baseWhere, ['order_status' => 7])),
|
|
'cancelled_orders' => $riderOrderModel->getCount(array_merge($baseWhere, ['order_status' => 8])),
|
|
];
|
|
|
|
// 计算完成率
|
|
$stats['completion_rate'] = $stats['total_orders'] > 0 ?
|
|
round(($stats['completed_orders'] / $stats['total_orders']) * 100, 2) : 0;
|
|
|
|
// 计算平均配送时间
|
|
$avgDeliveryTime = $riderOrderModel->where(array_merge($baseWhere, ['order_status' => 6]))
|
|
->avg('TIMESTAMPDIFF(MINUTE, accept_time, delivery_time)');
|
|
$stats['avg_delivery_time'] = round($avgDeliveryTime ?: 0, 2);
|
|
|
|
$this->ajaxReturn(callBack(statusCode()['success'], '获取统计数据成功', $stats));
|
|
}
|
|
} |