547 行
18 KiB
PHP
547 行
18 KiB
PHP
<?php
|
|
namespace Retail\Controller;
|
|
use Think\Controller;
|
|
|
|
/**
|
|
* 骑手区域管理控制器
|
|
*/
|
|
class RiderAreaController extends Controller {
|
|
|
|
/**
|
|
* 区域管理首页
|
|
*/
|
|
public function index() {
|
|
$this->display();
|
|
}
|
|
|
|
/**
|
|
* 获取配送区域列表
|
|
*/
|
|
public function getAreaList() {
|
|
$page = I('page', 1, 'intval');
|
|
$pageSize = I('pageSize', 20, 'intval');
|
|
$keyword = I('keyword', '', 'trim');
|
|
$status = I('status', '', 'trim');
|
|
$city_id = I('city_id', 0, 'intval');
|
|
|
|
$where = array();
|
|
|
|
// 关键词搜索
|
|
if (!empty($keyword)) {
|
|
$where['area_name'] = array('like', '%' . $keyword . '%');
|
|
}
|
|
|
|
// 状态筛选
|
|
if ($status !== '') {
|
|
$where['status'] = $status;
|
|
}
|
|
|
|
// 城市筛选
|
|
if ($city_id > 0) {
|
|
$where['city_id'] = $city_id;
|
|
}
|
|
|
|
$model = M('delivery_area');
|
|
$total = $model->where($where)->count();
|
|
|
|
$list = $model->where($where)
|
|
->order('create_time desc')
|
|
->page($page, $pageSize)
|
|
->select();
|
|
|
|
// 格式化数据
|
|
foreach ($list as &$item) {
|
|
$item['status_text'] = $this->getStatusText($item['status']);
|
|
$item['create_time_format'] = date('Y-m-d H:i:s', $item['create_time']);
|
|
|
|
// 获取城市名称
|
|
$city = M('city')->where(array('id' => $item['city_id']))->find();
|
|
$item['city_name'] = $city ? $city['name'] : '';
|
|
|
|
// 统计该区域的骑手数量
|
|
$item['rider_count'] = M('rider_area')->where(array('area_id' => $item['id']))->count();
|
|
|
|
// 统计该区域的订单数量(今日)
|
|
$today_start = strtotime(date('Y-m-d 00:00:00'));
|
|
$today_end = strtotime(date('Y-m-d 23:59:59'));
|
|
$item['today_orders'] = M('order')->where(array(
|
|
'delivery_area_id' => $item['id'],
|
|
'create_time' => array('between', array($today_start, $today_end))
|
|
))->count();
|
|
}
|
|
|
|
$this->ajaxReturn(array(
|
|
'code' => 0,
|
|
'msg' => 'success',
|
|
'data' => array(
|
|
'list' => $list,
|
|
'total' => $total
|
|
)
|
|
));
|
|
}
|
|
|
|
/**
|
|
* 获取区域详情
|
|
*/
|
|
public function getAreaDetail() {
|
|
$id = I('id', 0, 'intval');
|
|
if (empty($id)) {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '参数错误'));
|
|
}
|
|
|
|
$detail = M('delivery_area')->where(array('id' => $id))->find();
|
|
if (empty($detail)) {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '区域不存在'));
|
|
}
|
|
|
|
// 格式化数据
|
|
$detail['status_text'] = $this->getStatusText($detail['status']);
|
|
$detail['create_time_format'] = date('Y-m-d H:i:s', $detail['create_time']);
|
|
|
|
// 获取城市信息
|
|
$city = M('city')->where(array('id' => $detail['city_id']))->find();
|
|
$detail['city_info'] = $city;
|
|
|
|
// 解析区域坐标
|
|
if (!empty($detail['coordinates'])) {
|
|
$detail['coordinate_list'] = json_decode($detail['coordinates'], true);
|
|
} else {
|
|
$detail['coordinate_list'] = array();
|
|
}
|
|
|
|
// 获取区域统计
|
|
$stats = $this->getSingleAreaStats($id);
|
|
$detail['stats'] = $stats;
|
|
|
|
$this->ajaxReturn(array(
|
|
'code' => 0,
|
|
'msg' => 'success',
|
|
'data' => $detail
|
|
));
|
|
}
|
|
|
|
/**
|
|
* 添加/编辑配送区域
|
|
*/
|
|
public function saveArea() {
|
|
$id = I('id', 0, 'intval');
|
|
$data = array(
|
|
'area_name' => I('area_name', '', 'trim'),
|
|
'city_id' => I('city_id', 0, 'intval'),
|
|
'coordinates' => I('coordinates', '', 'trim'),
|
|
'center_lat' => I('center_lat', 0, 'floatval'),
|
|
'center_lng' => I('center_lng', 0, 'floatval'),
|
|
'radius' => I('radius', 0, 'floatval'),
|
|
'delivery_fee' => I('delivery_fee', 0, 'floatval'),
|
|
'min_order_amount' => I('min_order_amount', 0, 'floatval'),
|
|
'description' => I('description', '', 'trim'),
|
|
'status' => I('status', 1, 'intval')
|
|
);
|
|
|
|
// 数据验证
|
|
if (empty($data['area_name'])) {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '区域名称不能为空'));
|
|
}
|
|
if ($data['city_id'] <= 0) {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '请选择城市'));
|
|
}
|
|
if (empty($data['coordinates'])) {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '区域坐标不能为空'));
|
|
}
|
|
|
|
$model = M('delivery_area');
|
|
|
|
if ($id > 0) {
|
|
// 编辑
|
|
$data['update_time'] = time();
|
|
$result = $model->where(array('id' => $id))->save($data);
|
|
} else {
|
|
// 添加
|
|
$data['create_time'] = time();
|
|
$data['update_time'] = time();
|
|
$result = $model->add($data);
|
|
}
|
|
|
|
if ($result !== false) {
|
|
$this->ajaxReturn(array('code' => 0, 'msg' => '操作成功'));
|
|
} else {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '操作失败'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 删除配送区域
|
|
*/
|
|
public function deleteArea() {
|
|
$id = I('id', 0, 'intval');
|
|
if (empty($id)) {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '参数错误'));
|
|
}
|
|
|
|
// 检查是否有骑手绑定
|
|
$riderCount = M('rider_area')->where(array('area_id' => $id))->count();
|
|
if ($riderCount > 0) {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '该区域已有骑手绑定,无法删除'));
|
|
}
|
|
|
|
$result = M('delivery_area')->where(array('id' => $id))->delete();
|
|
if ($result) {
|
|
$this->ajaxReturn(array('code' => 0, 'msg' => '删除成功'));
|
|
} else {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '删除失败'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 启用/禁用区域
|
|
*/
|
|
public function toggleStatus() {
|
|
$id = I('id', 0, 'intval');
|
|
$status = I('status', 0, 'intval');
|
|
|
|
if (empty($id)) {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '参数错误'));
|
|
}
|
|
|
|
$result = M('delivery_area')->where(array('id' => $id))->save(array(
|
|
'status' => $status,
|
|
'update_time' => time()
|
|
));
|
|
|
|
if ($result !== false) {
|
|
$this->ajaxReturn(array('code' => 0, 'msg' => '操作成功'));
|
|
} else {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '操作失败'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取骑手区域分配列表
|
|
*/
|
|
public function getRiderAreaList() {
|
|
$page = I('page', 1, 'intval');
|
|
$pageSize = I('pageSize', 20, 'intval');
|
|
$keyword = I('keyword', '', 'trim');
|
|
$area_id = I('area_id', 0, 'intval');
|
|
|
|
$where = array();
|
|
|
|
// 关键词搜索
|
|
if (!empty($keyword)) {
|
|
$where['_string'] = "(u.nickname LIKE '%{$keyword}%' OR u.phone LIKE '%{$keyword}%')";
|
|
}
|
|
|
|
// 区域筛选
|
|
if ($area_id > 0) {
|
|
$where['ra.area_id'] = $area_id;
|
|
}
|
|
|
|
$model = M('rider_area');
|
|
$total = $model->alias('ra')
|
|
->join('ls_users u ON ra.rider_id = u.id')
|
|
->join('ls_delivery_area da ON ra.area_id = da.id')
|
|
->where($where)
|
|
->count();
|
|
|
|
$list = $model->alias('ra')
|
|
->field('ra.*, u.nickname, u.phone, u.avatar, da.area_name')
|
|
->join('ls_users u ON ra.rider_id = u.id')
|
|
->join('ls_delivery_area da ON ra.area_id = da.id')
|
|
->where($where)
|
|
->order('ra.create_time desc')
|
|
->page($page, $pageSize)
|
|
->select();
|
|
|
|
// 格式化数据
|
|
foreach ($list as &$item) {
|
|
$item['create_time_format'] = date('Y-m-d H:i:s', $item['create_time']);
|
|
|
|
// 统计该骑手在该区域的订单数量
|
|
$item['order_count'] = M('order')->where(array(
|
|
'rider_id' => $item['rider_id'],
|
|
'delivery_area_id' => $item['area_id']
|
|
))->count();
|
|
}
|
|
|
|
$this->ajaxReturn(array(
|
|
'code' => 0,
|
|
'msg' => 'success',
|
|
'data' => array(
|
|
'list' => $list,
|
|
'total' => $total
|
|
)
|
|
));
|
|
}
|
|
|
|
/**
|
|
* 分配骑手到区域
|
|
*/
|
|
public function assignRiderToArea() {
|
|
$rider_id = I('rider_id', 0, 'intval');
|
|
$area_ids = I('area_ids', '', 'trim');
|
|
|
|
if ($rider_id <= 0) {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '请选择骑手'));
|
|
}
|
|
|
|
if (empty($area_ids)) {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '请选择配送区域'));
|
|
}
|
|
|
|
$area_id_array = explode(',', $area_ids);
|
|
|
|
// 先删除该骑手的所有区域分配
|
|
M('rider_area')->where(array('rider_id' => $rider_id))->delete();
|
|
|
|
// 重新分配区域
|
|
$success_count = 0;
|
|
foreach ($area_id_array as $area_id) {
|
|
$area_id = intval($area_id);
|
|
if ($area_id <= 0) continue;
|
|
|
|
$data = array(
|
|
'rider_id' => $rider_id,
|
|
'area_id' => $area_id,
|
|
'create_time' => time()
|
|
);
|
|
|
|
$result = M('rider_area')->add($data);
|
|
if ($result) {
|
|
$success_count++;
|
|
}
|
|
}
|
|
|
|
$this->ajaxReturn(array(
|
|
'code' => 0,
|
|
'msg' => "分配成功,共分配{$success_count}个区域"
|
|
));
|
|
}
|
|
|
|
/**
|
|
* 移除骑手区域分配
|
|
*/
|
|
public function removeRiderArea() {
|
|
$id = I('id', 0, 'intval');
|
|
if (empty($id)) {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '参数错误'));
|
|
}
|
|
|
|
$result = M('rider_area')->where(array('id' => $id))->delete();
|
|
if ($result) {
|
|
$this->ajaxReturn(array('code' => 0, 'msg' => '移除成功'));
|
|
} else {
|
|
$this->ajaxReturn(array('code' => 1, 'msg' => '移除失败'));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取区域热力图数据
|
|
*/
|
|
public function getHeatmapData() {
|
|
$area_id = I('area_id', 0, 'intval');
|
|
$date = I('date', date('Y-m-d'), 'trim');
|
|
|
|
$start_time = strtotime($date . ' 00:00:00');
|
|
$end_time = strtotime($date . ' 23:59:59');
|
|
|
|
$where = array(
|
|
'create_time' => array('between', array($start_time, $end_time))
|
|
);
|
|
|
|
if ($area_id > 0) {
|
|
$where['delivery_area_id'] = $area_id;
|
|
}
|
|
|
|
// 获取订单分布数据
|
|
$orders = M('order')->field('pickup_lat, pickup_lng, delivery_lat, delivery_lng')
|
|
->where($where)
|
|
->select();
|
|
|
|
$heatmap_data = array();
|
|
|
|
foreach ($orders as $order) {
|
|
// 取餐点
|
|
if (!empty($order['pickup_lat']) && !empty($order['pickup_lng'])) {
|
|
$heatmap_data[] = array(
|
|
'lat' => floatval($order['pickup_lat']),
|
|
'lng' => floatval($order['pickup_lng']),
|
|
'type' => 'pickup',
|
|
'weight' => 1
|
|
);
|
|
}
|
|
|
|
// 配送点
|
|
if (!empty($order['delivery_lat']) && !empty($order['delivery_lng'])) {
|
|
$heatmap_data[] = array(
|
|
'lat' => floatval($order['delivery_lat']),
|
|
'lng' => floatval($order['delivery_lng']),
|
|
'type' => 'delivery',
|
|
'weight' => 1
|
|
);
|
|
}
|
|
}
|
|
|
|
$this->ajaxReturn(array(
|
|
'code' => 0,
|
|
'msg' => 'success',
|
|
'data' => $heatmap_data
|
|
));
|
|
}
|
|
|
|
/**
|
|
* 导出区域数据
|
|
*/
|
|
public function exportArea() {
|
|
$keyword = I('keyword', '', 'trim');
|
|
$status = I('status', '', 'trim');
|
|
$city_id = I('city_id', 0, 'intval');
|
|
|
|
$where = array();
|
|
|
|
if (!empty($keyword)) {
|
|
$where['area_name'] = array('like', '%' . $keyword . '%');
|
|
}
|
|
if ($status !== '') {
|
|
$where['status'] = $status;
|
|
}
|
|
if ($city_id > 0) {
|
|
$where['city_id'] = $city_id;
|
|
}
|
|
|
|
$list = M('delivery_area')->where($where)->order('create_time desc')->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', '状态');
|
|
$objActSheet->setCellValue('I1', '骑手数量');
|
|
$objActSheet->setCellValue('J1', '创建时间');
|
|
|
|
// 填充数据
|
|
$row = 2;
|
|
foreach ($list as $item) {
|
|
$city = M('city')->where(array('id' => $item['city_id']))->find();
|
|
$rider_count = M('rider_area')->where(array('area_id' => $item['id']))->count();
|
|
|
|
$objActSheet->setCellValue('A' . $row, $item['id']);
|
|
$objActSheet->setCellValue('B' . $row, $item['area_name']);
|
|
$objActSheet->setCellValue('C' . $row, $city ? $city['name'] : '');
|
|
$objActSheet->setCellValue('D' . $row, $item['center_lat'] . ',' . $item['center_lng']);
|
|
$objActSheet->setCellValue('E' . $row, $item['radius'] . 'km');
|
|
$objActSheet->setCellValue('F' . $row, $item['delivery_fee']);
|
|
$objActSheet->setCellValue('G' . $row, $item['min_order_amount']);
|
|
$objActSheet->setCellValue('H' . $row, $this->getStatusText($item['status']));
|
|
$objActSheet->setCellValue('I' . $row, $rider_count);
|
|
$objActSheet->setCellValue('J' . $row, date('Y-m-d H:i:s', $item['create_time']));
|
|
$row++;
|
|
}
|
|
|
|
$filename = '配送区域_' . date('YmdHis') . '.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;
|
|
}
|
|
|
|
/**
|
|
* 获取区域统计数据
|
|
*/
|
|
public function getAreaStats() {
|
|
$city_id = I('city_id', 0, 'intval');
|
|
|
|
$where = array();
|
|
if ($city_id > 0) {
|
|
$where['city_id'] = $city_id;
|
|
}
|
|
|
|
$model = M('delivery_area');
|
|
|
|
$stats = array(
|
|
'total_count' => $model->where($where)->count(),
|
|
'active_count' => $model->where(array_merge($where, array('status' => 1)))->count(),
|
|
'inactive_count' => $model->where(array_merge($where, array('status' => 0)))->count()
|
|
);
|
|
|
|
// 统计骑手分配情况
|
|
$stats['total_riders'] = M('rider_area')->alias('ra')
|
|
->join('ls_delivery_area da ON ra.area_id = da.id')
|
|
->where($where)
|
|
->count('DISTINCT ra.rider_id');
|
|
|
|
// 统计今日订单
|
|
$today_start = strtotime(date('Y-m-d 00:00:00'));
|
|
$today_end = strtotime(date('Y-m-d 23:59:59'));
|
|
$stats['today_orders'] = M('order')->alias('o')
|
|
->join('ls_delivery_area da ON o.delivery_area_id = da.id')
|
|
->where(array_merge($where, array(
|
|
'o.create_time' => array('between', array($today_start, $today_end))
|
|
)))
|
|
->count();
|
|
|
|
$this->ajaxReturn(array(
|
|
'code' => 0,
|
|
'msg' => 'success',
|
|
'data' => $stats
|
|
));
|
|
}
|
|
|
|
/**
|
|
* 获取单个区域统计数据
|
|
*/
|
|
private function getSingleAreaStats($area_id) {
|
|
$stats = array();
|
|
|
|
// 骑手数量
|
|
$stats['rider_count'] = M('rider_area')->where(array('area_id' => $area_id))->count();
|
|
|
|
// 今日订单
|
|
$today_start = strtotime(date('Y-m-d 00:00:00'));
|
|
$today_end = strtotime(date('Y-m-d 23:59:59'));
|
|
$stats['today_orders'] = M('order')->where(array(
|
|
'delivery_area_id' => $area_id,
|
|
'create_time' => array('between', array($today_start, $today_end))
|
|
))->count();
|
|
|
|
// 本月订单
|
|
$month_start = strtotime(date('Y-m-01 00:00:00'));
|
|
$month_end = strtotime(date('Y-m-t 23:59:59'));
|
|
$stats['month_orders'] = M('order')->where(array(
|
|
'delivery_area_id' => $area_id,
|
|
'create_time' => array('between', array($month_start, $month_end))
|
|
))->count();
|
|
|
|
// 平均配送时间
|
|
$stats['avg_delivery_time'] = M('order')->where(array(
|
|
'delivery_area_id' => $area_id,
|
|
'status' => 'completed'
|
|
))->avg('delivery_duration') / 60; // 转换为分钟
|
|
|
|
return $stats;
|
|
}
|
|
|
|
/**
|
|
* 获取状态文本
|
|
*/
|
|
private function getStatusText($status) {
|
|
$statusMap = array(
|
|
0 => '已禁用',
|
|
1 => '已启用'
|
|
);
|
|
return isset($statusMap[$status]) ? $statusMap[$status] : '未知';
|
|
}
|
|
}
|
|
?>
|