display(); } /** * 骑手编辑页面 */ public function edit() { $id = I('get.id', 0, 'intval'); if ($id) { $this->assign('rider_id', $id); } $this->display(); } /** * 骑手详情页面 */ public function detail() { $id = I('get.id', 0, 'intval'); if ($id) { $this->assign('rider_id', $id); } $this->display(); } public function getStatistics() { try { $Rider = M('RiderInfo'); $Order = M('retail_pay_order'); // 修改为正确的订单表名 // 总骑手数 $totalRiders = $Rider->where(array('status' => 1))->count(); // 在线骑手数 $onlineRiders = $Rider->where(array('work_status' => 1, 'status' => 1))->count(); // 今日订单数 - 使用支付订单表统计 $today = date('Y-m-d'); $todayStart = strtotime($today . ' 00:00:00'); $todayEnd = strtotime($today . ' 23:59:59'); $todayOrders = $Order->where(array( 'add_time' => array('BETWEEN', array($todayStart, $todayEnd)), 'order_state' => 'SUCCESS' ))->count(); // 今日收入 - 统计今日成功支付的订单金额 $todayRevenue = $Order->where(array( 'add_time' => array('BETWEEN', array($todayStart, $todayEnd)), 'order_state' => 'SUCCESS' ))->sum('trade_amount'); $result = array( 'code' => 1, 'message' => 'success', 'data' => array( 'totalRiders' => $totalRiders, 'onlineRiders' => $onlineRiders, 'todayOrders' => $todayOrders, 'todayRevenue' => number_format($todayRevenue, 2) ) ); $this->ajaxReturn($result); } catch (\Exception $e) { $this->ajaxReturn(array( 'code' => 0, 'message' => $e->getMessage(), 'data' => null )); } } /** * 获取骑手列表 */ public function getRiderList() { try { $page = I('get.page', 1, 'intval'); $pageSize = I('get.pageSize', 20, 'intval'); $keyword = I('get.keyword', '', 'trim'); $status = I('get.status', '', 'trim'); $certification = I('get.certification', '', 'trim'); $where = array(); // 关键词搜索 if (!empty($keyword)) { $where['_string'] = "(rider_name LIKE '%{$keyword}%' OR rider_phone LIKE '%{$keyword}%')"; } // 状态筛选 if (!empty($status)) { $where['status'] = $status; } // 认证状态筛选 if (is_numeric($certification)) { $where['auth_status'] = (int)$certification; } $where['status'] = 1; $Rider = M('RiderInfo'); // 获取总数 $totalCount = $Rider->where($where)->count(); // 获取列表数据 $riders = $Rider->where($where) ->order('add_time DESC') ->page($page, $pageSize) ->select(); // 处理数据 foreach ($riders as &$rider) { // 获取今日订单数和收入 $todayStats = $this->getTodayStats($rider['id']); $rider['today_orders'] = $todayStats['orders']; $rider['today_income'] = $todayStats['income']; // 获取总订单数和总收入 $totalStats = $this->getTotalStats($rider['id']); $rider['total_orders'] = $totalStats['orders']; $rider['total_income'] = $totalStats['income']; // 格式化头像路径 if (!empty($rider['avatar'])) { $rider['avatar'] = __ROOT__ . $rider['avatar']; } } $result = array( 'code' => 1, 'message' => 'success', 'data' => array( 'list' => $riders, 'total' => $totalCount, 'page' => $page, 'pageSize' => $pageSize ) ); $this->ajaxReturn($result); } catch (\Exception $e) { $this->ajaxReturn(array( 'code' => 0, 'message' => $e->getMessage(), 'data' => array() )); } } /** * 搜索骑手 - 用于分配订单时的骑手搜索 */ public function searchRiders() { try { $keyword = I('post.keyword', '', 'trim'); $page = I('post.page', 1, 'intval'); $size = I('post.size', 20, 'intval'); if (empty($keyword) || strlen($keyword) < 2) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '搜索关键词至少需要2个字符', 'data' => array('list' => [], 'total' => 0) )); return; } $where = array(); $where['status'] = 1; // 只搜索正常状态的骑手 $where['auth_status'] = 2; // 只搜索已认证的骑手 // 按姓名或手机号搜索 $where['_string'] = "(rider_name LIKE '%{$keyword}%' OR rider_phone LIKE '%{$keyword}%')"; $Rider = M('RiderInfo'); // 获取总数 $totalCount = $Rider->where($where)->count(); // 获取列表数据 $riders = $Rider->where($where) ->field('id, rider_name, rider_phone, rider_avatar as avatar, work_status, rating') ->order('work_status DESC, rating DESC, add_time DESC') // 优先显示在线骑手和高评分骑手 ->page($page, $size) ->select(); // 处理数据 foreach ($riders as &$rider) { // 格式化头像路径 if (!empty($rider['avatar'])) { $rider['avatar'] = __ROOT__ . $rider['avatar']; } else { $rider['avatar'] = ''; } // 统一字段名,确保前端兼容 $rider['name'] = $rider['rider_name']; $rider['phone'] = $rider['rider_phone']; // 格式化工作状态 $rider['work_status'] = $rider['work_status'] == 1 ? 'online' : 'offline'; // 格式化评分 $rider['rating'] = floatval($rider['rating']); } $result = array( 'code' => 1, 'msg' => 'success', 'data' => array( 'list' => $riders, 'total' => $totalCount, 'page' => $page, 'size' => $size ) ); $this->ajaxReturn($result); } catch (\Exception $e) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '搜索失败:' . $e->getMessage(), 'data' => array('list' => [], 'total' => 0) )); } } /** * 获取骑手信息接口 - 用于页面数据显示 */ public function getRiderInfo() { $rider_id = I('post.id', 0, 'intval'); if (!$rider_id) { $this->ajaxReturn(['code' => 0, 'msg' => '参数错误', 'data' => null]); } $RiderModel = new RiderModel(); $ImageGalleryModel = new ImageGalleryModel(); // 构建查询条件 $map = array( 'id' => $rider_id, 'merchant_id' => $this->merchant_id ); if ($this->store_id) { $map['store_id'] = $this->store_id; } // 获取骑手基本信息 $rider = $RiderModel->getFind($map); if (empty($rider)) { $this->ajaxReturn(['code' => 0, 'msg' => '骑手不存在', 'data' => null]); } // 处理图片路径 if (!empty($rider['rider_avatar'])) { $rider['avatar_url'] = $ImageGalleryModel->getOneField(['id' => $rider['rider_avatar']], 'path'); } else { $rider['avatar_url'] = ''; } if (!empty($rider['id_card_front'])) { $rider['id_card_front_url'] = $ImageGalleryModel->getOneField(['id' => $rider['id_card_front']], 'path'); } else { $rider['id_card_front_url'] = ''; } if (!empty($rider['id_card_back'])) { $rider['id_card_back_url'] = $ImageGalleryModel->getOneField(['id' => $rider['id_card_back']], 'path'); } else { $rider['id_card_back_url'] = ''; } if (!empty($rider['health_cert'])) { $rider['health_cert_url'] = $ImageGalleryModel->getOneField(['id' => $rider['health_cert']], 'path'); } else { $rider['health_cert_url'] = ''; } // 格式化状态文本 $rider['work_status_text'] = $this->getWorkStatusText($rider['work_status']); $rider['auth_status_text'] = $this->getAuthStatusText($rider['auth_status']); $rider['health_status_text'] = $this->getHealthStatusText($rider['health_status']); $rider['status_text'] = $rider['status'] == 1 ? '正常' : '禁用'; // 获取统计数据 $stats = $this->getRiderStats($rider_id); $rider['today_orders'] = $stats['today_orders']; $rider['today_income'] = $stats['today_income']; $rider['total_income'] = $stats['total_income']; // 格式化时间显示 if (!empty($rider['add_time'])) { $rider['add_time_formatted'] = date('Y-m-d H:i:s', strtotime($rider['add_time'])); } if (!empty($rider['last_login_time'])) { $rider['last_login_time_formatted'] = date('Y-m-d H:i:s', strtotime($rider['last_login_time'])); } if (!empty($rider['auth_time'])) { $rider['auth_time_formatted'] = date('Y-m-d H:i:s', strtotime($rider['auth_time'])); } if (!empty($rider['health_time'])) { $rider['health_time_formatted'] = date('Y-m-d H:i:s', strtotime($rider['health_time'])); } // 格式化金额显示 $rider['balance_formatted'] = number_format($rider['balance'], 2); $rider['frozen_balance_formatted'] = number_format($rider['frozen_balance'], 2); $this->ajaxReturn(['code' => 1, 'msg' => '获取成功', 'data' => $rider]); } /** * 获取工作状态文本 */ private function getWorkStatusText($status) { $statusMap = [ 0 => '离线', 1 => '在线', 2 => '忙碌' ]; return isset($statusMap[$status]) ? $statusMap[$status] : '未知'; } /** * 获取认证状态文本 */ private function getAuthStatusText($status) { $statusMap = [ 0 => '未认证', 1 => '认证中', 2 => '已认证', 3 => '认证失败' ]; return isset($statusMap[$status]) ? $statusMap[$status] : '未知'; } /** * 获取健康证状态文本 */ private function getHealthStatusText($status) { $statusMap = [ 0 => '待审核', 1 => '已通过', 2 => '已拒绝' ]; return isset($statusMap[$status]) ? $statusMap[$status] : '未知'; } /** * 获取骑手统计数据 */ private function getRiderStats($rider_id) { $RiderOrder = M('RiderOrder'); $today = date('Y-m-d'); // 今日订单数 $today_orders = $RiderOrder->where([ 'rider_id' => $rider_id, 'add_time' => ['like', $today . '%'], 'order_status' => 6 // 已送达 ])->count(); // 今日收入 $today_income = $RiderOrder->where([ 'rider_id' => $rider_id, 'add_time' => ['like', $today . '%'], 'order_status' => 6 ])->sum('delivery_fee'); // 总收入 $total_income = $RiderOrder->where([ 'rider_id' => $rider_id, 'order_status' => 6 ])->sum('delivery_fee'); return [ 'today_orders' => $today_orders ?: 0, 'today_income' => number_format($today_income ?: 0, 2), 'total_income' => number_format($total_income ?: 0, 2) ]; } /** * 获取骑手详情 */ public function getRiderDetail() { $rider_id = I('post.rider_id', 0); if (!$rider_id) { $this->ajaxReturn(['code' => 0, 'msg' => '参数错误']); } $RiderModel = new RiderModel(); $ImageGalleryModel = new ImageGalleryModel(); $map = array( 'id' => $rider_id, 'merchant_id' => $this->merchant_id, 'status' => array('neq', 3) ); if ($this->store_id) { $map['store_id'] = $this->store_id; } $rider = $RiderModel->getFind($map); if (empty($rider)) { $this->ajaxReturn(['code' => 0, 'msg' => '骑手不存在']); } // 处理图片路径 if ($rider['rider_avatar']) { $rider['avatar_url'] = $ImageGalleryModel->getOneField(['id' => $rider['rider_avatar']], 'path'); } if ($rider['id_card_front']) { $rider['id_card_front_url'] = $ImageGalleryModel->getOneField(['id' => $rider['id_card_front']], 'path'); } if ($rider['id_card_back']) { $rider['id_card_back_url'] = $ImageGalleryModel->getOneField(['id' => $rider['id_card_back']], 'path'); } if ($rider['health_certificate']) { $rider['health_certificate_url'] = $ImageGalleryModel->getOneField(['id' => $rider['health_certificate']], 'path'); } $this->ajaxReturn(['code' => 1, 'data' => $rider]); } /** * 添加骑手 */ public function addRider() { try { // 获取店铺ID,优先使用表单提交的值 $post_store_id = I('post.store_id', -1, 'intval'); $store_id = 0; if ($post_store_id >= 0) { // 表单提交了 store_id $store_id = $post_store_id; // 如果 store_id > 0,验证店铺是否存在 if ($store_id > 0) { $StoreModel = M('retail_store'); $store = $StoreModel->where([ 'id' => $store_id, 'merchant_id' => $this->merchant_id, 'status' => 1 // 只允许绑定启用状态的店铺 ])->find(); if (empty($store)) { $this->ajaxReturn(['code' => 0, 'msg' => '店铺不存在或已禁用']); } } } else { // 未提交 store_id,使用管理员的店铺ID $store_id = $this->store_id ? $this->store_id : 0; } $data = array( 'merchant_id' => $this->merchant_id, 'store_id' => $store_id, 'rider_name' => I('post.rider_name', ''), 'rider_phone' => I('post.rider_phone', ''), 'id_card' => I('post.id_card', ''), 'rider_avatar' => I('post.rider_avatar', ''), 'id_card_front' => I('post.id_card_front', ''), 'id_card_back' => I('post.id_card_back', ''), 'health_cert' => I('post.health_cert', ''), 'health_cert_expire' => I('post.health_cert_expire', ''), 'emergency_name' => I('post.emergency_name', ''), 'emergency_phone' => I('post.emergency_phone', ''), 'emergency_relation' => I('post.emergency_relation', ''), 'work_status' => I('post.work_status', ''), // 默认离线 'auth_status' => I('post.auth_status', ''), // 默认未认证 'health_status' => I('post.health_status', ''), // 默认待审核 'balance' => 0.00, 'frozen_balance' => 0.00, 'total_orders' => 0, 'completed_orders' => 0, 'rating' => 5.00, 'on_time_rate' => 100.00, 'order_notification' => 1, 'voice_broadcast' => 1, 'vibration_alert' => 1, 'auto_accept_order' => 0, 'max_distance' => 5.00, 'min_delivery_fee' => 0.00, 'max_orders_per_time' => 3, 'accept_order_types' => '1,2,3', 'reject_low_rating_store' => 0, 'min_store_rating' => 0.00, 'auto_accept_start_time' => '00:00', 'auto_accept_end_time' => '23:59', 'default_withdraw_type' => 1, 'add_time' => date('Y-m-d H:i:s'), 'status' => 1 // 默认正常状态 ); // 验证必填字段 if (empty($data['rider_name'])) { $this->ajaxReturn(['code' => 0, 'msg' => '请输入骑手姓名']); } if (empty($data['rider_phone'])) { $this->ajaxReturn(['code' => 0, 'msg' => '请输入骑手手机号']); } // 验证手机号格式 if (!preg_match('/^1[3-9]\d{9}$/', $data['rider_phone'])) { $this->ajaxReturn(['code' => 0, 'msg' => '手机号格式不正确']); } // 检查手机号是否已存在 $RiderModel = M('rider_info'); $existRider = $RiderModel->where(array( 'rider_phone' => $data['rider_phone'], 'merchant_id' => $this->merchant_id, 'status' => array('neq', 0) // 排除已删除的记录 ))->find(); if ($existRider) { $this->ajaxReturn(['code' => 0, 'msg' => '该手机号已被注册']); } // 生成默认密码和盐值 $salt = substr(md5(time() . rand(1000, 9999)), 0, 8); $password = '123456'; // 默认密码 $data['salt'] = $salt; $data['password'] = $this->encryptPassword($password, $salt); $result = $RiderModel->add($data); if ($result) { $this->ajaxReturn(['code' => 1, 'msg' => '添加成功', 'rider_id' => $result]); } else { $this->ajaxReturn(['code' => 0, 'msg' => '添加失败']); } } catch (\Exception $e) { $this->ajaxReturn(['code' => 0, 'msg' => $e->getMessage()]); } } /** * 编辑骑手 */ public function editRider() { $rider_id = I('post.rider_id', 0); if (!$rider_id) { $this->ajaxReturn(['code' => 0, 'msg' => '参数错误']); } $RiderModel = new RiderModel(); $map = array( 'id' => $rider_id, 'merchant_id' => $this->merchant_id, 'status' => array('neq', 3) ); if ($this->store_id) { $map['store_id'] = $this->store_id; } $rider = $RiderModel->getFind($map); if (empty($rider)) { $this->ajaxReturn(['code' => 0, 'msg' => '骑手不存在']); } $data = array( 'rider_name' => I('post.rider_name', ''), 'rider_phone' => I('post.rider_phone', ''), 'id_card' => I('post.id_card', ''), 'emergency_contact' => I('post.emergency_contact', ''), 'emergency_phone' => I('post.emergency_phone', ''), 'work_status' => I('post.work_status', 1) ); // 验证必填字段 if (empty($data['rider_name'])) { $this->ajaxReturn(['code' => 0, 'msg' => '请输入骑手姓名']); } if (empty($data['rider_phone'])) { $this->ajaxReturn(['code' => 0, 'msg' => '请输入骑手手机号']); } // 验证手机号格式 if (!preg_match('/^1[3-9]\d{9}$/', $data['rider_phone'])) { $this->ajaxReturn(['code' => 0, 'msg' => '手机号格式不正确']); } // 检查手机号是否被其他骑手使用 if ($data['rider_phone'] != $rider['rider_phone']) { $existRider = $RiderModel->getRiderByPhone($data['rider_phone'], $this->merchant_id); if ($existRider && $existRider['id'] != $rider_id) { $this->ajaxReturn(['code' => 0, 'msg' => '该手机号已被其他骑手使用']); } } // 处理图片字段 $avatar = I('post.rider_avatar', 0); $id_card_front = I('post.id_card_front', 0); $id_card_back = I('post.id_card_back', 0); $health_certificate = I('post.health_certificate', 0); if ($avatar) $data['rider_avatar'] = $avatar; if ($id_card_front) $data['id_card_front'] = $id_card_front; if ($id_card_back) $data['id_card_back'] = $id_card_back; if ($health_certificate) $data['health_certificate'] = $health_certificate; $result = $RiderModel->updateOrder($map, $data); if ($result !== false) { $this->ajaxReturn(['code' => 1, 'msg' => '更新成功']); } else { $this->ajaxReturn(['code' => 0, 'msg' => '更新失败']); } } /** * 删除骑手 */ public function deleteRider() { try { $rider_id = I('post.id', 0); if (!$rider_id) { $this->ajaxReturn(['code' => 0, 'msg' => '参数错误']); } $RiderModel = new RiderModel(); $map = array( 'id' => $rider_id, 'merchant_id' => $this->merchant_id ); if ($this->store_id) { $map['store_id'] = $this->store_id; } $data = array( 'status' => 3 ); $result = $RiderModel->updateOrder($map, $data); if ($result) { $this->ajaxReturn(['code' => 1, 'msg' => '删除成功']); } else { $this->ajaxReturn(['code' => 0, 'msg' => '删除失败']); } } catch (\Exception $e) { $this->ajaxReturn(['code' => 0, 'msg' => $e->getMessage()]); } } /** * 认证审核 */ public function auth_review() { if (IS_POST) { $rider_id = I('post.rider_id', 0); $auth_status = I('post.auth_status', 0); if (!$rider_id) { $this->ajaxReturn(['code' => 0, 'msg' => '参数错误']); } if (!in_array($auth_status, [2, 3])) { $this->ajaxReturn(['code' => 0, 'msg' => '认证状态参数错误']); } $RiderModel = new RiderModel(); $map = array( 'id' => $rider_id, 'merchant_id' => $this->merchant_id, 'status' => array('neq', 3) ); if ($this->store_id) { $map['store_id'] = $this->store_id; } $rider = $RiderModel->getFind($map); if (empty($rider)) { $this->ajaxReturn(['code' => 0, 'msg' => '骑手不存在']); } $result = $RiderModel->updateAuthStatus($rider_id, $auth_status); if ($result) { $status_text = $auth_status == 2 ? '认证通过' : '认证失败'; $this->ajaxReturn(['code' => 1, 'msg' => $status_text]); } else { $this->ajaxReturn(['code' => 0, 'msg' => '操作失败']); } } else { $id = I('get.id', 0, 'intval'); if ($id) { $this->assign('rider_id', $id); } $this->display(); } } /** * 更新骑手状态 */ public function updateStatus() { $rider_id = I('post.rider_id', 0); $status = I('post.status', 1); if (!$rider_id) { $this->ajaxReturn(['code' => 0, 'msg' => '参数错误']); } if (!in_array($status, [1, 2])) { $this->ajaxReturn(['code' => 0, 'msg' => '状态参数错误']); } $RiderModel = new RiderModel(); $map = array( 'id' => $rider_id, 'merchant_id' => $this->merchant_id ); if ($this->store_id) { $map['store_id'] = $this->store_id; } $data = array( 'status' => $status ); $result = $RiderModel->updateOrder($map, $data); if ($result) { $status_text = $status == 1 ? '启用' : '禁用'; $this->ajaxReturn(['code' => 1, 'msg' => $status_text . '成功']); } else { $this->ajaxReturn(['code' => 0, 'msg' => '操作失败']); } } /** * 文件上传 */ public function upload() { $upload = new \Think\Upload(); $upload->maxSize = 2097152; // 2MB $upload->exts = array('jpg', 'gif', 'png', 'jpeg'); $upload->rootPath = './Uploads/'; $upload->savePath = 'rider/'; $upload->saveName = array('uniqid', ''); $upload->autoSub = true; $upload->subName = array('date', 'Ymd'); $info = $upload->upload(); if (!$info) { $this->ajaxReturn(array('code' => 0, 'message' => $upload->getError())); } else { $file = array_shift($info); $filePath = '/Uploads/' . $file['savepath'] . $file['savename']; $this->ajaxReturn(array('code' => 1, 'message' => '上传成功', 'data' => array('url' => $filePath))); } } /** * 导出骑手数据 */ public function exportRiders() { $keyword = I('get.keyword', '', 'trim'); $status = I('get.status', '', 'trim'); $certification = I('get.certification', '', 'trim'); $where = array('status' => 1); // 关键词搜索 if (!empty($keyword)) { $where['_string'] = "(rider_name LIKE '%{$keyword}%' OR rider_phone LIKE '%{$keyword}%')"; } // 状态筛选 if (!empty($status)) { $where['work_status'] = $status === 'online' ? 1 : 0; } // 认证状态筛选 if (!empty($certification)) { $where['auth_status'] = $certification === 'certified' ? 1 : ($certification === 'pending' ? 0 : 2); } $Rider = M('RiderInfo'); $riders = $Rider->where($where)->order('add_time DESC')->select(); // 处理数据 foreach ($riders as &$rider) { $todayStats = $this->getTodayStats($rider['id']); $rider['today_orders'] = $todayStats['orders']; $rider['today_income'] = $todayStats['income']; $totalStats = $this->getTotalStats($rider['id']); $rider['total_orders'] = $totalStats['orders']; $rider['total_income'] = $totalStats['income']; } // 生成CSV文件 $filename = '骑手数据_' . date('YmdHis') . '.csv'; header('Content-Type: application/csv'); header('Content-Disposition: attachment; filename="' . $filename . '"'); $output = fopen('php://output', 'w'); // 写入BOM,解决中文乱码 fwrite($output, "\xEF\xBB\xBF"); // 写入表头 fputcsv($output, array('姓名', '手机号', '身份证号', '车牌号', '状态', '认证状态', '今日订单', '今日收入', '总订单', '总收入', '评分', '注册时间')); // 写入数据 foreach ($riders as $rider) { fputcsv($output, array( $rider['name'], $rider['phone'], $rider['id_card'], $rider['vehicle_number'], $this->getStatusText($rider['status']), $this->getCertificationText($rider['certification']), $rider['today_orders'], $rider['today_income'], $rider['total_orders'], $rider['total_income'], $rider['rating'], $rider['created_at'] )); } fclose($output); exit; } /** * 获取今日统计数据 */ private function getTodayStats($riderId) { $Order = M('rider_order'); $today = date('Y-m-d'); $where = array( 'rider_id' => $riderId, 'add_time' => array('LIKE', $today . '%') ); $orders = $Order->where($where)->count(); $income = $Order->where(array_merge($where, array('order_status' => 6)))->sum('delivery_fee'); // 6表示已送达 return array( 'orders' => $orders ?: 0, 'income' => $income ?: 0 ); } /** * 获取总统计数据 */ private function getTotalStats($riderId) { $Order = M('rider_order'); $where = array( 'rider_id' => $riderId ); $orders = $Order->where($where)->count(); $income = $Order->where(array_merge($where, array('order_status' => 6)))->sum('delivery_fee'); // 6表示已送达 return array( 'orders' => $orders ?: 0, 'income' => $income ?: 0 ); } /** * 获取状态文本 */ private function getStatusText($status) { $texts = array( 'online' => '在线', 'offline' => '离线', 'busy' => '忙碌' ); return isset($texts[$status]) ? $texts[$status] : '未知'; } /** * 获取认证状态文本 */ private function getCertificationText($certification) { $texts = array( 'certified' => '已认证', 'pending' => '待认证', 'rejected' => '认证失败' ); return isset($texts[$certification]) ? $texts[$certification] : '未知'; } /** * 重置骑手密码 */ public function password_reset() { if (IS_POST) { $id = I('post.id', 0, 'intval'); $newPassword = I('post.new_password', '', 'trim'); if (empty($id)) { $this->ajaxReturn(array('code' => 0, 'message' => '参数错误')); } if (empty($newPassword)) { $this->ajaxReturn(array('code' => 0, 'message' => '请输入新密码')); } if (strlen($newPassword) < 6) { $this->ajaxReturn(array('code' => 0, 'message' => '密码长度不能少于6位')); } $Rider = M('RiderInfo'); $rider = $Rider->where(array('id' => $id, 'status' => 1))->find(); if (!$rider) { $this->ajaxReturn(array('code' => 404, 'message' => '骑手不存在')); } // 生成新的盐值和加密密码 $salt = $this->generateSalt(); $encryptedPassword = $this->encryptPassword($newPassword, $salt); $result = $Rider->where(array('id' => $id))->save(array( 'password' => $encryptedPassword, 'salt' => $salt, 'update_time' => date('Y-m-d H:i:s') )); if ($result !== false) { $this->ajaxReturn(array('code' => 1, 'message' => '密码重置成功')); } else { $this->ajaxReturn(array('code' => 0, 'message' => '密码重置失败')); } } else { $id = I('get.id', 0, 'intval'); if ($id) { $this->assign('rider_id', $id); } $this->display(); } } /** * 修改骑手密码 */ public function changePassword() { if (IS_POST) { $id = I('post.id', 0, 'intval'); $oldPassword = I('post.old_password', '', 'trim'); $newPassword = I('post.new_password', '', 'trim'); if (empty($id) || empty($oldPassword) || empty($newPassword)) { $this->ajaxReturn(array('code' => 0, 'message' => '参数不完整')); } if (strlen($newPassword) < 6) { $this->ajaxReturn(array('code' => 0, 'message' => '新密码长度不能少于6位')); } $Rider = M('RiderInfo'); $rider = $Rider->where(array('id' => $id, 'status' => 1))->find(); if (!$rider) { $this->ajaxReturn(array('code' => 404, 'message' => '骑手不存在')); } // 验证旧密码 if (!$this->verifyPassword($oldPassword, $rider['password'], $rider['salt'])) { $this->ajaxReturn(array('code' => 0, 'message' => '原密码错误')); } // 生成新的盐值和加密新密码 $newSalt = $this->generateSalt(); $newEncryptedPassword = $this->encryptPassword($newPassword, $newSalt); $result = $Rider->where(array('id' => $id))->save(array( 'password' => $newEncryptedPassword, 'salt' => $newSalt, 'update_time' => date('Y-m-d H:i:s') )); if ($result !== false) { $this->ajaxReturn(array('code' => 1, 'message' => '密码修改成功')); } else { $this->ajaxReturn(array('code' => 0, 'message' => '密码修改失败')); } } } /** * 重置骑手密码 - 对应前端调用的resetPassword接口 */ public function resetPassword() { if (IS_POST) { $id = I('post.id', 0, 'intval'); $newPassword = I('post.new_password', '', 'trim'); if (empty($id)) { $this->ajaxReturn(array('code' => 0, 'message' => '参数错误')); } if (empty($newPassword)) { $this->ajaxReturn(array('code' => 0, 'message' => '请输入新密码')); } if (strlen($newPassword) < 6) { $this->ajaxReturn(array('code' => 0, 'message' => '密码长度不能少于6位')); } $Rider = M('RiderInfo'); $rider = $Rider->where(array('id' => $id, 'status' => 1))->find(); if (!$rider) { $this->ajaxReturn(array('code' => 404, 'message' => '骑手不存在')); } // 生成新的盐值和加密密码 $salt = $this->generateSalt(); $encryptedPassword = $this->encryptPassword($newPassword, $salt); $result = $Rider->where(array('id' => $id))->save(array( 'password' => $encryptedPassword, 'salt' => $salt, 'update_time' => date('Y-m-d H:i:s') )); if ($result !== false) { $this->ajaxReturn(array('code' => 1, 'message' => '密码重置成功')); } else { $this->ajaxReturn(array('code' => 0, 'message' => '密码重置失败')); } } } /** * 生成密码盐值 * @return string */ private function generateSalt() { return substr(md5(uniqid(rand(), true)), 0, 10); } /** * 加密密码 * @param string $password 原始密码 * @param string $salt 盐值 * @return string */ private function encryptPassword($password, $salt) { return md5(md5($password) . $salt); } /** * 更新骑手信息接口 */ public function updateRider() { // 检查请求方法 if (!IS_POST) { $this->ajaxReturn(['code' => 0, 'msg' => '请求方法错误']); } $rider_id = I('post.id', 0, 'intval'); if (!$rider_id) { $this->ajaxReturn(['code' => 0, 'msg' => '参数错误:骑手ID不能为空']); } $RiderModel = new RiderModel(); // 构建查询条件 $map = array( 'id' => $rider_id, 'merchant_id' => $this->merchant_id, 'status' => array('neq', 3) // 排除已删除状态 ); if ($this->store_id) { $map['store_id'] = $this->store_id; } // 检查骑手是否存在 $rider = $RiderModel->getFind($map); if (empty($rider)) { $this->ajaxReturn(['code' => 0, 'msg' => '骑手不存在或无权限操作']); } // 获取要更新的字段 $updateData = array(); // 基本信息字段 $rider_name = I('post.rider_name', '', 'trim'); if (!empty($rider_name)) { // 防止XSS攻击 $rider_name = htmlspecialchars($rider_name, ENT_QUOTES, 'UTF-8'); if (mb_strlen($rider_name) < 2 || mb_strlen($rider_name) > 20) { $this->ajaxReturn(['code' => 0, 'msg' => '骑手姓名长度应在2-20个字符之间']); } // 检查是否包含特殊字符 if (preg_match('/[<>"\'\&]/', $rider_name)) { $this->ajaxReturn(['code' => 0, 'msg' => '骑手姓名不能包含特殊字符']); } $updateData['rider_name'] = $rider_name; } $rider_phone = I('post.rider_phone', '', 'trim'); if (!empty($rider_phone)) { if (!preg_match('/^1[3-9]\d{9}$/', $rider_phone)) { $this->ajaxReturn(['code' => 0, 'msg' => '手机号格式不正确']); } // 只有当手机号发生变化时才检查是否被其他骑手使用 if ($rider['rider_phone'] !== $rider_phone) { $existingRider = $RiderModel->getFind([ 'rider_phone' => $rider_phone, 'id' => array('neq', $rider_id), 'merchant_id' => $this->merchant_id, 'status' => array('neq', 3) ]); if (!empty($existingRider)) { $this->ajaxReturn(['code' => 0, 'msg' => '该手机号已被其他骑手使用']); } } $updateData['rider_phone'] = $rider_phone; } $id_card = I('post.id_card', '', 'trim'); if (!empty($id_card)) { if (!preg_match('/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/', $id_card)) { $this->ajaxReturn(['code' => 0, 'msg' => '身份证号格式不正确']); } // 只有当身份证号发生变化时才检查是否被其他骑手使用 if ($rider['id_card'] !== $id_card) { $existingRider = $RiderModel->getFind([ 'id_card' => $id_card, 'id' => array('neq', $rider_id), 'merchant_id' => $this->merchant_id, 'status' => array('neq', 3) ]); if (!empty($existingRider)) { $this->ajaxReturn(['code' => 0, 'msg' => '该身份证号已被其他骑手使用']); } } $updateData['id_card'] = $id_card; } // 图片字段验证 $ImageGalleryModel = new ImageGalleryModel(); $rider_avatar = I('post.rider_avatar', 0, 'intval'); if ($rider_avatar > 0) { // 验证图片是否存在 $image = $ImageGalleryModel->getFind(['id' => $rider_avatar]); if (empty($image)) { $this->ajaxReturn(['code' => 0, 'msg' => '头像图片不存在']); } $updateData['rider_avatar'] = $rider_avatar; } $id_card_front = I('post.id_card_front', 0, 'intval'); if ($id_card_front > 0) { $image = $ImageGalleryModel->getFind(['id' => $id_card_front]); if (empty($image)) { $this->ajaxReturn(['code' => 0, 'msg' => '身份证正面图片不存在']); } $updateData['id_card_front'] = $id_card_front; } $id_card_back = I('post.id_card_back', 0, 'intval'); if ($id_card_back > 0) { $image = $ImageGalleryModel->getFind(['id' => $id_card_back]); if (empty($image)) { $this->ajaxReturn(['code' => 0, 'msg' => '身份证反面图片不存在']); } $updateData['id_card_back'] = $id_card_back; } $health_cert = I('post.health_cert', 0, 'intval'); if ($health_cert > 0) { $image = $ImageGalleryModel->getFind(['id' => $health_cert]); if (empty($image)) { $this->ajaxReturn(['code' => 0, 'msg' => '健康证图片不存在']); } $updateData['health_cert'] = $health_cert; } // 健康证过期时间 $health_cert_expire = I('post.health_cert_expire', '', 'trim'); if (!empty($health_cert_expire)) { $expire_time = strtotime($health_cert_expire); if (!$expire_time) { $this->ajaxReturn(['code' => 0, 'msg' => '健康证过期时间格式不正确']); } // 检查过期时间不能是过去的时间 if ($expire_time < time()) { $this->ajaxReturn(['code' => 0, 'msg' => '健康证过期时间不能是过去的时间']); } $updateData['health_cert_expire'] = $health_cert_expire; } // 紧急联系人信息 $emergency_name = I('post.emergency_name', '', 'trim'); if (!empty($emergency_name)) { $emergency_name = htmlspecialchars($emergency_name, ENT_QUOTES, 'UTF-8'); if (mb_strlen($emergency_name) < 2 || mb_strlen($emergency_name) > 20) { $this->ajaxReturn(['code' => 0, 'msg' => '紧急联系人姓名长度应在2-20个字符之间']); } if (preg_match('/[<>"\'\&]/', $emergency_name)) { $this->ajaxReturn(['code' => 0, 'msg' => '紧急联系人姓名不能包含特殊字符']); } $updateData['emergency_name'] = $emergency_name; } $emergency_phone = I('post.emergency_phone', '', 'trim'); if (!empty($emergency_phone)) { if (!preg_match('/^1[3-9]\d{9}$/', $emergency_phone)) { $this->ajaxReturn(['code' => 0, 'msg' => '紧急联系人手机号格式不正确']); } $updateData['emergency_phone'] = $emergency_phone; } $emergency_relation = I('post.emergency_relation', '', 'trim'); if (!empty($emergency_relation)) { $emergency_relation = htmlspecialchars($emergency_relation, ENT_QUOTES, 'UTF-8'); if (mb_strlen($emergency_relation) > 20) { $this->ajaxReturn(['code' => 0, 'msg' => '紧急联系人关系长度不能超过20个字符']); } $updateData['emergency_relation'] = $emergency_relation; } // 绑定店铺 $store_id = I('post.store_id', -1, 'intval'); if ($store_id >= 0) { // 如果 store_id > 0,验证店铺是否存在 if ($store_id > 0) { $StoreModel = M('retail_store'); $store = $StoreModel->where([ 'id' => $store_id, 'merchant_id' => $this->merchant_id, 'status' => 1 // 只允许绑定启用状态的店铺 ])->find(); if (empty($store)) { $this->ajaxReturn(['code' => 0, 'msg' => '店铺不存在或已禁用']); } } $updateData['store_id'] = $store_id; } // 工作状态 $work_status = I('post.work_status', -1, 'intval'); if ($work_status >= 0 && $work_status <= 2) { $updateData['work_status'] = $work_status; } // 状态 $status = I('post.status', -1, 'intval'); if ($status >= 0 && $status <= 2) { $updateData['status'] = $status; } // 位置信息 $last_location_lat = I('post.last_location_lat', '', 'trim'); if (!empty($last_location_lat)) { if (!is_numeric($last_location_lat) || $last_location_lat < -90 || $last_location_lat > 90) { $this->ajaxReturn(['code' => 0, 'msg' => '纬度值不正确']); } $updateData['last_location_lat'] = floatval($last_location_lat); } $last_location_lng = I('post.last_location_lng', '', 'trim'); if (!empty($last_location_lng)) { if (!is_numeric($last_location_lng) || $last_location_lng < -180 || $last_location_lng > 180) { $this->ajaxReturn(['code' => 0, 'msg' => '经度值不正确']); } $updateData['last_location_lng'] = floatval($last_location_lng); } // 如果没有要更新的数据 if (empty($updateData)) { $this->ajaxReturn(['code' => 0, 'msg' => '没有要更新的数据']); } // 执行更新 try { $result = $RiderModel->updateOrder($map, $updateData); if ($result !== false) { // 记录操作日志 $this->addOperationLog('update_rider', $rider_id, '更新骑手信息', json_encode($updateData, JSON_UNESCAPED_UNICODE)); $this->ajaxReturn(['code' => 1, 'msg' => '更新成功', 'data' => ['updated_fields' => array_keys($updateData)]]); } else { $this->ajaxReturn(['code' => 0, 'msg' => '更新失败']); } } catch (\Exception $e) { // 记录错误日志 error_log('UpdateRider Error: ' . $e->getMessage() . ' - Data: ' . json_encode($updateData)); $this->ajaxReturn(['code' => 0, 'msg' => $e->getMessage()]); } } /** * 添加操作日志 */ private function addOperationLog($action, $rider_id, $description, $data = '') { $logData = array( 'admin_id' => session('admin_id') ?: 0, 'action' => $action, 'rider_id' => $rider_id, 'description' => $description, 'data' => $data, 'ip_address' => get_client_ip(), 'add_time' => date('Y-m-d H:i:s') ); try { M('RiderOperationLog')->add($logData); } catch (\Exception $e) { // 日志记录失败不影响主要操作 } } /** * 验证密码 * @param string $password 输入的密码 * @param string $encryptedPassword 数据库中的加密密码 * @param string $salt 盐值 * @return bool */ private function verifyPassword($password, $encryptedPassword, $salt) { return $this->encryptPassword($password, $salt) === $encryptedPassword; } /** * 配置管理页面 */ public function config() { $this->display('Rider/config'); } /** * 获取配置信息 */ public function getConfig() { $configModel = M('RiderConfig'); $config = $configModel->where(array('merchant_id' => $this->merchant_id))->find(); if (!$config) { // 创建默认配置 $defaultConfig = array( 'merchant_id' => $this->merchant_id, 'auto_sync_enabled' => 0, // 默认关闭自动同步 'delivery_fee_type' => 1, // 1:按距离计算 2:固定费用 3:阶梯计费 'base_fee' => 5.00, // 基础配送费 'distance_fee' => 1.50, // 每公里费用 'free_distance' => 3, // 免费配送距离(公里) 'max_distance' => 20, // 最大配送距离(公里) 'time_fee_enabled' => 1, // 是否启用时间加价 'night_fee_rate' => 0.5, // 夜间加价比例 'night_start_time' => '22:00', // 夜间开始时间 'night_end_time' => '06:00', // 夜间结束时间 'weather_fee_enabled' => 1, // 是否启用恶劣天气加价 'weather_fee_rate' => 1.0, // 恶劣天气加价 'urgent_fee_enabled' => 1, // 是否启用加急费 'urgent_fee' => 3.00, // 加急费用 'min_order_amount' => 20.00, // 最小起送金额 'packaging_fee' => 2.00, // 包装费 'created_time' => time(), 'updated_time' => time() ); $configModel->add($defaultConfig); $config = $defaultConfig; } $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => $config )); } /** * 保存配置 */ public function saveConfig() { $data = I('post.'); $configModel = M('RiderConfig'); // 验证数据 if (!isset($data['auto_sync_enabled']) || !isset($data['delivery_fee_type'])) { $this->ajaxReturn(array('code' => 0, 'msg' => '参数不完整')); } // 数据处理 $saveData = array( 'auto_sync_enabled' => intval($data['auto_sync_enabled']), 'delivery_fee_type' => intval($data['delivery_fee_type']), 'base_fee' => floatval($data['base_fee']), 'distance_fee' => floatval($data['distance_fee']), 'free_distance' => floatval($data['free_distance']), 'max_distance' => floatval($data['max_distance']), 'time_fee_enabled' => intval($data['time_fee_enabled']), 'night_fee_rate' => floatval($data['night_fee_rate']), 'night_start_time' => $data['night_start_time'], 'night_end_time' => $data['night_end_time'], 'weather_fee_enabled' => intval($data['weather_fee_enabled']), 'weather_fee_rate' => floatval($data['weather_fee_rate']), 'urgent_fee_enabled' => intval($data['urgent_fee_enabled']), 'urgent_fee' => floatval($data['urgent_fee']), 'min_order_amount' => floatval($data['min_order_amount']), 'packaging_fee' => floatval($data['packaging_fee']), 'updated_time' => time() ); $where = array('merchant_id' => $this->merchant_id); $exists = $configModel->where($where)->find(); if ($exists) { $result = $configModel->where($where)->save($saveData); } else { $saveData['merchant_id'] = $this->merchant_id; $saveData['created_time'] = time(); $result = $configModel->add($saveData); } if ($result !== false) { $this->ajaxReturn(array('code' => 1, 'msg' => '保存成功')); } else { $this->ajaxReturn(array('code' => 0, 'msg' => '保存失败')); } } /** * 获取配送费用计算规则 */ public function getFeeRules() { $configModel = M('RiderConfig'); $config = $configModel->where(array('merchant_id' => $this->merchant_id))->find(); if (!$config) { $this->ajaxReturn(array('code' => 0, 'msg' => '请先配置基础设置')); } $rules = array( 'delivery_fee_type' => $config['delivery_fee_type'], 'base_fee' => $config['base_fee'], 'distance_fee' => $config['distance_fee'], 'free_distance' => $config['free_distance'], 'max_distance' => $config['max_distance'], 'time_fee_enabled' => $config['time_fee_enabled'], 'night_fee_rate' => $config['night_fee_rate'], 'night_start_time' => $config['night_start_time'], 'night_end_time' => $config['night_end_time'], 'weather_fee_enabled' => $config['weather_fee_enabled'], 'weather_fee_rate' => $config['weather_fee_rate'], 'urgent_fee_enabled' => $config['urgent_fee_enabled'], 'urgent_fee' => $config['urgent_fee'], 'min_order_amount' => $config['min_order_amount'], 'packaging_fee' => $config['packaging_fee'] ); $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => $rules )); } /** * 计算配送费用 */ public function calculateDeliveryFee() { $distance = floatval(I('post.distance')); // 配送距离(公里) $orderAmount = floatval(I('post.order_amount')); // 订单金额 $isUrgent = intval(I('post.is_urgent', 0)); // 是否加急 $isNight = intval(I('post.is_night', 0)); // 是否夜间 $isBadWeather = intval(I('post.is_bad_weather', 0)); // 是否恶劣天气 $configModel = M('RiderConfig'); $config = $configModel->where(array('merchant_id' => $this->merchant_id))->find(); if (!$config) { $this->ajaxReturn(array('code' => 0, 'msg' => '请先配置基础设置')); } // 检查最大配送距离 if ($distance > $config['max_distance']) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '超出最大配送距离(' . $config['max_distance'] . '公里)' )); } // 检查最小起送金额 if ($orderAmount < $config['min_order_amount']) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '未达到最小起送金额(' . $config['min_order_amount'] . '元)' )); } // 初始化各项费用 $baseFee = floatval($config['base_fee']); $distanceFee = 0; $urgentFee = 0; $nightFee = 0; $weatherFee = 0; $packagingFee = floatval($config['packaging_fee']); $deliveryFee = 0; $feeDetails = array(); // 基础配送费计算 switch ($config['delivery_fee_type']) { case 1: // 按距离计算 $deliveryFee = $baseFee; $feeDetails[] = '基础费用: ' . $baseFee . '元'; if ($distance > $config['free_distance']) { $extraDistance = $distance - $config['free_distance']; $distanceFee = $extraDistance * $config['distance_fee']; $deliveryFee += $distanceFee; $feeDetails[] = '距离费用: ' . $extraDistance . '公里 × ' . $config['distance_fee'] . '元 = ' . $distanceFee . '元'; } break; case 2: // 固定费用 $deliveryFee = $baseFee; $feeDetails[] = '固定配送费: ' . $baseFee . '元'; break; case 3: // 阶梯计费 // 可以根据需要实现更复杂的阶梯计费逻辑 $deliveryFee = $baseFee; $feeDetails[] = '阶梯计费: ' . $baseFee . '元'; break; } // 包装费 $deliveryFee += $packagingFee; $feeDetails[] = '包装费: ' . $packagingFee . '元'; // 加急费 if ($isUrgent && $config['urgent_fee_enabled']) { $urgentFee = floatval($config['urgent_fee']); $deliveryFee += $urgentFee; $feeDetails[] = '加急费: ' . $urgentFee . '元'; } // 恶劣天气加价(固定金额) if ($isBadWeather && $config['weather_fee_enabled']) { $weatherFee = floatval($config['weather_fee_rate']); $deliveryFee += $weatherFee; $feeDetails[] = '恶劣天气加价: ' . $weatherFee . '元'; } // 夜间加价(按比例计算,基于当前总费用) if ($isNight && $config['time_fee_enabled']) { $nightFee = $deliveryFee * floatval($config['night_fee_rate']); $deliveryFee += $nightFee; $feeDetails[] = '夜间加价: ' . (floatval($config['night_fee_rate']) * 100) . '% = ' . $nightFee . '元'; } $this->ajaxReturn(array( 'code' => 1, 'msg' => '计算成功', 'data' => array( 'total' => round($deliveryFee, 2), 'baseFee' => round($baseFee, 2), 'distanceFee' => round($distanceFee, 2), 'urgentFee' => round($urgentFee, 2), 'nightFee' => round($nightFee, 2), 'weatherFee' => round($weatherFee, 2), 'packagingFee' => round($packagingFee, 2), 'fee_details' => $feeDetails, 'distance' => $distance, 'order_amount' => $orderAmount ) )); } /** * 测试配送费用计算 */ public function testFeeCalculation() { // 直接调用calculateDeliveryFee方法进行计算 $this->calculateDeliveryFee(); } }