1683 行
57 KiB
PHP
1683 行
57 KiB
PHP
<?php
|
||
|
||
namespace Retail\Controller;
|
||
|
||
use Retail\Model\RiderModel;
|
||
use Common\Model\ImageGalleryModel;
|
||
|
||
//[1.8.50]
|
||
|
||
/**
|
||
* 骑手管理控制器
|
||
* Class RiderController
|
||
* @package Retail\Controller
|
||
*/
|
||
class RiderController extends BaseController
|
||
{
|
||
/**
|
||
* 骑手管理主页
|
||
*/
|
||
public function index()
|
||
{
|
||
$this->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();
|
||
}
|
||
}
|