riderId; // 获取骑手基本信息 $riderModel = M('rider_info'); $rider = $riderModel->field('balance, total_orders, completed_orders')->find($riderId); if (!$rider) { $this->ajaxReturn(array('code' => 0, 'msg' => '骑手不存在')); } // 获取收入模型 $incomeModel = M('rider_income'); // 计算总收入(所有已结算的收入) $totalIncome = $incomeModel->where(array( 'rider_id' => $riderId, 'status' => 1 ))->sum('amount'); // 获取今日收入 $today = date('Y-m-d'); $todayIncome = $incomeModel->where(array( 'rider_id' => $riderId, 'status' => 1, 'settle_time' => array('like', $today . '%') ))->sum('amount'); // 获取本周收入 $weekStart = date('Y-m-d', strtotime('this week')); $weekEnd = date('Y-m-d', strtotime('this week +6 days')); $weekIncome = $incomeModel->where(array( 'rider_id' => $riderId, 'status' => 1, 'settle_time' => array('between', array($weekStart . ' 00:00:00', $weekEnd . ' 23:59:59')) ))->sum('amount'); // 获取本月收入 $monthStart = date('Y-m-01'); $monthEnd = date('Y-m-t'); $monthIncome = $incomeModel->where(array( 'rider_id' => $riderId, 'status' => 1, 'settle_time' => array('between', array($monthStart . ' 00:00:00', $monthEnd . ' 23:59:59')) ))->sum('amount'); // 获取今日订单数 $orderModel = M('rider_order'); $todayOrders = $orderModel->where(array( 'rider_id' => $riderId, 'order_status' => 6, 'delivery_time' => array('like', $today . '%') ))->count(); $data = array( 'balance' => floatval($rider['balance']), 'total_income' => floatval($totalIncome), 'today_income' => floatval($todayIncome), 'week_income' => floatval($weekIncome), 'month_income' => floatval($monthIncome), 'total_orders' => intval($rider['total_orders']), 'completed_orders' => intval($rider['completed_orders']), 'today_orders' => intval($todayOrders) ); $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => $data )); } catch (\Exception $e) { $this->ajaxReturn(array('code' => 0, 'msg' => $e->getMessage())); } } /** * 获取收入列表 */ public function getIncomeList() { try { $riderId = $this->riderId; // 获取参数 $page = $this->getRequestParam('page', 1, 'intval'); $pageSize = $this->getRequestParam('page_size', 20, 'intval'); $timeRange = $this->getRequestParam('time_range', 'today', 'trim'); // 筛选参数 $incomeType = $this->getRequestParam('income_type', '', 'trim'); $status = $this->getRequestParam('status', '', 'trim'); $startDate = $this->getRequestParam('start_date', '', 'trim'); $endDate = $this->getRequestParam('end_date', '', 'trim'); $minAmount = $this->getRequestParam('min_amount', '', 'trim'); $maxAmount = $this->getRequestParam('max_amount', '', 'trim'); // 限制分页参数 $page = max(1, $page); $pageSize = min(50, max(1, $pageSize)); // 构建查询条件 $where = array('rider_id' => $riderId); // 收入类型筛选 if (!empty($incomeType)) { $where['income_type'] = intval($incomeType); } // 收入状态筛选 if ($status !== '') { $where['status'] = intval($status); } // 金额范围筛选 if (!empty($minAmount) && is_numeric($minAmount) && !empty($maxAmount) && is_numeric($maxAmount)) { // 同时有最小和最大金额 $where['amount'] = array('between', array(floatval($minAmount), floatval($maxAmount))); } elseif (!empty($minAmount) && is_numeric($minAmount)) { // 只有最小金额 $where['amount'] = array('egt', floatval($minAmount)); } elseif (!empty($maxAmount) && is_numeric($maxAmount)) { // 只有最大金额 $where['amount'] = array('elt', floatval($maxAmount)); } // 时间范围筛选 - 优先使用筛选中的日期范围 if (!empty($startDate) && !empty($endDate)) { // 处理时间戳格式的日期 if (is_numeric($startDate) && is_numeric($endDate)) { // 时间戳转换为日期字符串 $startDateStr = date('Y-m-d', intval($startDate) / 1000); $endDateStr = date('Y-m-d', intval($endDate) / 1000); } else { // 已经是日期字符串格式 $startDateStr = $startDate; $endDateStr = $endDate; } $where['add_time'] = array('between', array($startDateStr . ' 00:00:00', $endDateStr . ' 23:59:59')); } else { // 使用时间选项卡的范围 $timeCondition = $this->buildTimeCondition($timeRange, $startDate, $endDate); if ($timeCondition) { $where['add_time'] = $timeCondition; } } $incomeModel = M('rider_income'); // 调试日志 \Think\Log::write("收入列表请求参数: " . json_encode(array( 'income_type' => $incomeType, 'status' => $status, 'start_date' => $startDate, 'end_date' => $endDate, 'min_amount' => $minAmount, 'max_amount' => $maxAmount, 'time_range' => $timeRange )), 'INFO'); \Think\Log::write("收入列表筛选条件: " . json_encode($where), 'INFO'); // 获取总数 $total = $incomeModel->where($where)->count(); // 获取列表数据 $offset = ($page - 1) * $pageSize; $incomes = $incomeModel->where($where) ->order('add_time DESC') ->limit($offset, $pageSize) ->select(); // 格式化数据 $list = array(); foreach ($incomes as $income) { $item = array( 'id' => intval($income['id']), 'order_id' => intval($income['order_id']), 'income_type' => intval($income['income_type']), 'income_type_text' => $this->getIncomeTypeText($income['income_type']), 'amount' => floatval($income['amount']), 'description' => $income['description'] ?: '', 'settle_time' => $income['settle_time'] ?: '', 'add_time' => $income['add_time'], 'status' => intval($income['status']), 'status_text' => $this->getIncomeStatusText($income['status']) ); // 如果有关联订单,获取订单信息 if ($income['order_id'] > 0) { $orderModel = M('rider_order'); $order = $orderModel->field('order_no, store_name, customer_name') ->find($income['order_id']); if ($order) { $item['order_info'] = array( 'order_no' => $order['order_no'], 'store_name' => $order['store_name'], 'customer_name' => $order['customer_name'] ); } } $list[] = $item; } $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => array( 'list' => $list, 'total' => $total, 'page' => $page, 'page_size' => $pageSize, 'has_more' => ($page * $pageSize) < $total ) )); } catch (\Exception $e) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '服务器错误: ' . $e->getMessage() )); } } /** * 构建时间查询条件 */ private function buildTimeCondition($timeRange, $startDate = '', $endDate = '') { $today = date('Y-m-d'); switch ($timeRange) { case 'today': return array('between', array($today . ' 00:00:00', $today . ' 23:59:59')); case 'week': $weekStart = date('Y-m-d', strtotime('this week')); $weekEnd = date('Y-m-d', strtotime('this week +6 days')); return array('between', array($weekStart . ' 00:00:00', $weekEnd . ' 23:59:59')); case 'month': $monthStart = date('Y-m-01'); $monthEnd = date('Y-m-t'); return array('between', array($monthStart . ' 00:00:00', $monthEnd . ' 23:59:59')); case 'custom': if (!empty($startDate) && !empty($endDate)) { return array('between', array($startDate . ' 00:00:00', $endDate . ' 23:59:59')); } break; } return null; } /** * 获取收入明细 */ public function getIncomeDetail() { $riderId = $this->riderId; $page = $this->getRequestParam('page', 1); $limit = $this->getRequestParam('limit', 10); $type = $this->getRequestParam('type', ''); // 收入类型 $startDate = $this->getRequestParam('start_date', ''); $endDate = $this->getRequestParam('end_date', ''); $where = array('rider_id' => $riderId); if ($type !== '') { $where['income_type'] = $type; } if ($startDate && $endDate) { $where['settle_time'] = array('between', array($startDate . ' 00:00:00', $endDate . ' 23:59:59')); } $incomeModel = M('rider_income'); $offset = ($page - 1) * $limit; $incomes = $incomeModel->where($where) ->order('add_time DESC') ->limit($offset, $limit) ->select(); $total = $incomeModel->where($where)->count(); // 格式化数据 foreach ($incomes as &$income) { $income['amount'] = floatval($income['amount']); $income['income_type_text'] = $this->getIncomeTypeText($income['income_type']); $income['status_text'] = $this->getIncomeStatusText($income['status']); } $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => array( 'incomes' => $incomes, 'total' => $total, 'page' => $page, 'limit' => $limit ) )); } /** * 申请提现 */ public function applyWithdraw() { $riderId = $this->riderId; // 检查Content-Type并解析参数 $contentType = ''; if (isset($_SERVER['CONTENT_TYPE'])) { $contentType = $_SERVER['CONTENT_TYPE']; } // 初始化变量 $amount = 0; $bankCard = ''; $bankName = ''; $accountName = ''; if (strpos($contentType, 'application/json') !== false) { $input = json_decode(file_get_contents('php://input'), true); if (isset($input['amount'])) { $amount = floatval($input['amount']); } if (isset($input['bank_card'])) { $bankCard = trim($input['bank_card']); } if (isset($input['bank_name'])) { $bankName = trim($input['bank_name']); } if (isset($input['account_name'])) { $accountName = trim($input['account_name']); } } else { $amount = $this->getRequestParam('amount', 0); $bankCard = $this->getRequestParam('bank_card', ''); $bankName = $this->getRequestParam('bank_name', ''); $accountName = $this->getRequestParam('account_name', ''); } if (empty($amount) || $amount <= 0) { $this->ajaxReturn(array('code' => 0, 'msg' => '提现金额必须大于0')); } if (empty($bankCard)) { $this->ajaxReturn(array('code' => 0, 'msg' => '银行卡号不能为空')); } if (empty($bankName)) { $this->ajaxReturn(array('code' => 0, 'msg' => '银行名称不能为空')); } if (empty($accountName)) { $this->ajaxReturn(array('code' => 0, 'msg' => '账户姓名不能为空')); } // 检查骑手余额 $riderModel = M('rider_info'); $rider = $riderModel->field('balance, real_name')->find($riderId); if (!$rider) { $this->ajaxReturn(array('code' => 0, 'msg' => '骑手不存在')); } if ($rider['balance'] < $amount) { $this->ajaxReturn(array('code' => 0, 'msg' => '余额不足')); } // 验证账户姓名是否与实名认证一致 if ($rider['rider_name'] && $accountName !== $rider['rider_name']) { $this->ajaxReturn(array('code' => 0, 'msg' => '账户姓名必须与实名认证一致')); } // 创建提现记录 $withdrawModel = M('rider_withdraw'); $withdrawData = array( 'rider_id' => $riderId, 'amount' => $amount, 'bank_card' => $bankCard, 'bank_name' => $bankName, 'account_name' => $accountName, 'status' => 0, // 待审核 'add_time' => date('Y-m-d H:i:s') ); $withdrawId = $withdrawModel->add($withdrawData); if ($withdrawId) { // 获取当前余额 $currentRider = $riderModel->field('balance, frozen_balance')->find($riderId); $balanceBefore = floatval($currentRider['balance']); $frozenBefore = floatval($currentRider['frozen_balance']); // 冻结余额 $riderModel->where(array('id' => $riderId))->setDec('balance', $amount); // 记录余额变动日志 $balanceLog = array( 'rider_id' => $riderId, 'type' => 'withdraw_apply', 'amount' => $amount, 'balance_before' => $balanceBefore, 'balance_after' => $balanceBefore - $amount, 'frozen_before' => $frozenBefore, 'frozen_after' => $frozenBefore, // 这里应该是增加,但旧版本没有frozen_balance字段 'description' => '提现申请,从可用余额扣除', 'related_id' => $withdrawId, 'operator_id' => $riderId, 'add_time' => date('Y-m-d H:i:s') ); M('rider_balance_log')->add($balanceLog); $this->ajaxReturn(array( 'code' => 1, 'msg' => '提现申请提交成功,请等待审核', 'data' => array( 'withdraw_id' => $withdrawId ) )); } else { $this->ajaxReturn(array('code' => 0, 'msg' => '提现申请失败')); } } /** * 获取提现记录 */ public function getWithdrawHistory() { $riderId = $this->riderId; $page = $this->getRequestParam('page', 1); $limit = $this->getRequestParam('limit', 10); $status = $this->getRequestParam('status', ''); $where = array('rider_id' => $riderId); if ($status !== '') { $where['status'] = $status; } $withdrawModel = M('rider_withdraw'); $offset = ($page - 1) * $limit; $withdraws = $withdrawModel->where($where) ->order('add_time DESC') ->limit($offset, $limit) ->select(); $total = $withdrawModel->where($where)->count(); // 格式化数据 foreach ($withdraws as &$withdraw) { $withdraw['amount'] = floatval($withdraw['amount']); $withdraw['status_text'] = $this->getWithdrawStatusText($withdraw['status']); $withdraw['bank_card_masked'] = $this->maskBankCard($withdraw['bank_card']); } $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => array( 'withdraws' => $withdraws, 'total' => $total, 'page' => $page, 'limit' => $limit ) )); } /** * 获取收入统计 */ public function getIncomeStats() { $riderId = $this->riderId; $type = $this->getRequestParam('type', 'week'); // week, month, year $incomeModel = M('rider_income'); $stats = array(); switch ($type) { case 'week': // 最近7天 for ($i = 6; $i >= 0; $i--) { $date = date('Y-m-d', strtotime("-{$i} days")); $income = $incomeModel->where(array( 'rider_id' => $riderId, 'status' => 1, 'settle_time' => array('like', $date . '%') ))->sum('amount'); $stats[] = array( 'date' => $date, 'income' => floatval($income) ); } break; case 'month': // 最近30天 for ($i = 29; $i >= 0; $i--) { $date = date('Y-m-d', strtotime("-{$i} days")); $income = $incomeModel->where(array( 'rider_id' => $riderId, 'status' => 1, 'settle_time' => array('like', $date . '%') ))->sum('amount'); $stats[] = array( 'date' => $date, 'income' => floatval($income) ); } break; case 'year': // 最近12个月 for ($i = 11; $i >= 0; $i--) { $month = date('Y-m', strtotime("-{$i} months")); $income = $incomeModel->where(array( 'rider_id' => $riderId, 'status' => 1, 'settle_time' => array('like', $month . '%') ))->sum('amount'); $stats[] = array( 'month' => $month, 'income' => floatval($income) ); } break; } $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => $stats )); } /** * 获取收入类型文本 */ private function getIncomeTypeText($type) { $types = array( 1 => '配送费', 2 => '奖励', 3 => '补贴', 4 => '其他' ); if (isset($types[$type])) { return $types[$type]; } else { return '未知'; } } /** * 获取收入状态文本 */ private function getIncomeStatusText($status) { $statuses = array( 0 => '待结算', 1 => '已结算', 2 => '已提现' ); if (isset($statuses[$status])) { return $statuses[$status]; } else { return '未知'; } } /** * 获取账户余额信息 */ public function getBalance() { // 检查骑手登录状态 if (!$this->riderId) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '请先登录' )); } try { $riderModel = M('rider_info'); $riderInfo = $riderModel->where(array('id' => $this->riderId))->find(); if (!$riderInfo) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '骑手信息不存在' )); } // 计算可提现余额和冻结金额 $availableAmount = floatval($riderInfo['balance']); $frozenAmount = floatval($riderInfo['frozen_balance']); // 计算今日收入 $today = date('Y-m-d'); $incomeModel = M('rider_income'); $todayIncome = $incomeModel->where(array( 'rider_id' => $this->riderId, 'add_time' => array('like', $today . '%'), 'status' => 1 // 已结算 ))->sum('amount') ?: 0; // 计算本月收入 $thisMonth = date('Y-m'); $monthIncome = $incomeModel->where(array( 'rider_id' => $this->riderId, 'add_time' => array('like', $thisMonth . '%'), 'status' => 1 // 已结算 ))->sum('amount') ?: 0; $data = array( 'available_amount' => $availableAmount, 'frozen_amount' => $frozenAmount, 'total_balance' => $availableAmount + $frozenAmount, 'today_income' => floatval($todayIncome), 'month_income' => floatval($monthIncome) ); $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => $data )); } catch (\Exception $e) { \Think\Log::write("获取余额信息失败:" . $e->getMessage(), 'ERROR'); $this->ajaxReturn(array( 'code' => 0, 'msg' => '获取失败,请稍后重试' )); } } /** * 获取提现配置 */ public function getWithdrawConfig() { // 检查骑手登录状态 if (!$this->riderId) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '请先登录' )); } try { $configModel = M('rider_withdraw_config'); // 优先获取商户专属配置,如果没有则使用全局配置 $merchantId = isset($this->riderInfo['merchant_id']) ? $this->riderInfo['merchant_id'] : 0; $config = $configModel->where(array( 'merchant_id' => $merchantId, 'status' => 1 ))->find(); if (!$config) { // 使用全局配置 $config = $configModel->where(array( 'merchant_id' => 0, 'status' => 1 ))->find(); } if (!$config) { // 返回默认配置 $config = array( 'min_amount' => 1.00, 'max_amount' => 5000.00, 'daily_limit' => 3, 'alipay_enabled' => 1, 'alipay_fee_rate' => 0.0010, 'wechat_enabled' => 1, 'wechat_fee_rate' => 0.0010, 'bank_enabled' => 1, 'bank_fee_rate' => 0.0020, 'auto_process' => 0, 'process_time_start' => '09:00', 'process_time_end' => '18:00', 'weekend_process' => 0 ); } // 检查今日已提现次数 $today = date('Y-m-d'); $withdrawModel = M('rider_withdraw'); $todayCount = $withdrawModel->where(array( 'rider_id' => $this->riderId, 'add_time' => array('like', $today . '%') ))->count(); $data = array( 'min_amount' => floatval($config['min_amount']), 'max_amount' => floatval($config['max_amount']), 'daily_limit' => intval($config['daily_limit']), 'today_used' => intval($todayCount), 'can_withdraw_today' => $todayCount < $config['daily_limit'], 'methods' => array( 'alipay' => array( 'enabled' => intval($config['alipay_enabled']) === 1, 'fee_rate' => floatval($config['alipay_fee_rate']), 'fee_text' => (floatval($config['alipay_fee_rate']) * 100) . '%手续费' ), 'wechat' => array( 'enabled' => intval($config['wechat_enabled']) === 1, 'fee_rate' => floatval($config['wechat_fee_rate']), 'fee_text' => (floatval($config['wechat_fee_rate']) * 100) . '%手续费' ), 'bank' => array( 'enabled' => intval($config['bank_enabled']) === 1, 'fee_rate' => floatval($config['bank_fee_rate']), 'fee_text' => (floatval($config['bank_fee_rate']) * 100) . '%手续费' ) ), 'auto_process' => intval($config['auto_process']) === 1, 'process_time' => array( 'start' => $config['process_time_start'], 'end' => $config['process_time_end'], 'weekend' => intval($config['weekend_process']) === 1 ) ); $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => $data )); } catch (\Exception $e) { \Think\Log::write("获取提现配置失败:" . $e->getMessage(), 'ERROR'); $this->ajaxReturn(array( 'code' => 0, 'msg' => '获取失败,请稍后重试' )); } } /** * 获取提现记录 */ public function getWithdrawRecords() { // 检查骑手登录状态 if (!$this->riderId) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '请先登录' )); } try { // 获取分页参数 $page = $this->getRequestParam('page', 1, 'intval'); $limit = $this->getRequestParam('limit', 10, 'intval'); $status = $this->getRequestParam('status', '', 'trim'); // 添加状态筛选参数 // 限制分页参数 $page = max(1, $page); $limit = min(50, max(1, $limit)); $withdrawModel = M('rider_withdraw'); $where = array('rider_id' => $this->riderId); // 添加状态筛选 if (!empty($status) && $status !== 'all') { $backendStatus = $this->mapFrontendStatusToBackend($status); if ($backendStatus !== null) { $where['status'] = $backendStatus; } } // 获取总数 $total = $withdrawModel->where($where)->count(); // 获取列表数据 $offset = ($page - 1) * $limit; $records = $withdrawModel->where($where) ->order('add_time DESC') ->limit($offset, $limit) ->select(); // 格式化数据 $list = array(); foreach ($records as $record) { $item = array( 'id' => intval($record['id']), 'withdraw_no' => $record['withdraw_no'], 'order_no' => $record['withdraw_no'], // 前端使用 order_no 字段显示订单号 'amount' => floatval($record['amount']), 'fee' => floatval($record['fee']), 'actual_amount' => floatval($record['actual_amount']), 'withdraw_type' => intval($record['withdraw_type']), 'withdraw_type_text' => $this->getWithdrawTypeText($record['withdraw_type']), 'account_name' => $record['account_name'], 'account_number' => $this->maskAccountNumber($record['account_number']), 'card_number' => $this->maskAccountNumber($record['account_number']), // 前端使用 card_number 字段 'bank_name' => $this->getDisplayBankName($record), // 根据提现方式显示正确的名称 'bank_code' => $this->getBankCode($this->getDisplayBankName($record)), // 添加银行代码用于显示图标 'status' => $this->mapStatusToFrontend($record['status']), // 映射到前端状态 'status_text' => $this->getFrontendStatusText($record['status']), 'reject_reason' => $record['reject_reason'] ?: '', 'fail_reason' => $record['reject_reason'] ?: '', // 前端使用 fail_reason 字段 'process_time' => $record['process_time'] ?: '', 'complete_time' => $record['complete_time'] ?: '', 'processed_at' => $this->getProcessedTime($record), // 添加处理时间字段 'add_time' => $record['add_time'], 'created_at' => $record['add_time'] // 兼容前端字段名 ); $list[] = $item; } $data = array( 'list' => $list, 'total' => intval($total), 'page' => $page, 'limit' => $limit, 'pages' => ceil($total / $limit) ); $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => $data )); } catch (\Exception $e) { \Think\Log::write("获取提现记录失败:" . $e->getMessage(), 'ERROR'); $this->ajaxReturn(array( 'code' => 0, 'msg' => '获取失败,请稍后重试' )); } } /** * 获取提现方式文本 */ private function getWithdrawTypeText($type) { $types = array( 1 => '银行卡', 2 => '支付宝', 3 => '微信' ); return isset($types[$type]) ? $types[$type] : '未知'; } /** * 获取提现状态文本 */ private function getWithdrawStatusText($status) { $statuses = array( 0 => '申请中', 1 => '已完成', 2 => '已拒绝' ); return isset($statuses[$status]) ? $statuses[$status] : '未知'; } /** * 账号脱敏处理 */ private function maskAccountNumber($accountNumber) { if (strlen($accountNumber) <= 8) { return $accountNumber; } return substr($accountNumber, 0, 4) . '****' . substr($accountNumber, -4); } /** * 获取显示用的银行名称 */ private function getDisplayBankName($record) { $withdrawType = intval($record['withdraw_type']); switch ($withdrawType) { case 1: // 银行卡 return $record['bank_name'] ?: '银行卡'; case 2: // 支付宝 return '支付宝'; case 3: // 微信 return '微信'; default: return $record['bank_name'] ?: '未知方式'; } } /** * 根据银行名称获取银行代码 */ private function getBankCode($bankName) { $bankCodes = array( '中国工商银行' => 'ICBC', '工商银行' => 'ICBC', '中国农业银行' => 'ABC', '农业银行' => 'ABC', '中国银行' => 'BOC', '中国建设银行' => 'CCB', '建设银行' => 'CCB', '交通银行' => 'COMM', '招商银行' => 'CMB', '中信银行' => 'CITIC', '光大银行' => 'CEB', '民生银行' => 'CMBC', '广发银行' => 'GDB', '平安银行' => 'PAB', '兴业银行' => 'CIB', '支付宝' => 'ALIPAY', '微信' => 'WECHAT' ); return isset($bankCodes[$bankName]) ? $bankCodes[$bankName] : 'DEFAULT'; } /** * 映射后端状态到前端状态 */ private function mapStatusToFrontend($backendStatus) { $statusMap = array( 0 => 'processing', // 申请中 -> 处理中 1 => 'success', // 已完成 -> 已到账 2 => 'failed' // 已拒绝 -> 已失败 ); return isset($statusMap[$backendStatus]) ? $statusMap[$backendStatus] : 'processing'; } /** * 映射前端状态到后端状态 */ private function mapFrontendStatusToBackend($frontendStatus) { $statusMap = array( 'processing' => 0, // 处理中 -> 申请中 'success' => 1, // 已到账 -> 已完成 'failed' => 2 // 已失败 -> 已拒绝 ); return isset($statusMap[$frontendStatus]) ? $statusMap[$frontendStatus] : null; } /** * 获取前端状态文本 */ private function getFrontendStatusText($backendStatus) { $statusTexts = array( 0 => '处理中', 1 => '已到账', 2 => '已失败' ); return isset($statusTexts[$backendStatus]) ? $statusTexts[$backendStatus] : '未知'; } /** * 获取处理时间 */ private function getProcessedTime($record) { $status = intval($record['status']); // 根据状态返回对应的处理时间 switch ($status) { case 0: // 申请中 return ''; case 1: // 已完成 return $record['complete_time'] ?: ''; case 2: // 已拒绝 return $record['process_time'] ?: ''; default: return ''; } } /** * 获取提现详情 */ public function getWithdrawDetail() { // 检查骑手登录状态 if (!$this->riderId) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '请先登录' )); } try { // 获取提现记录ID $id = $this->getRequestParam('id', 0, 'intval'); if (!$id) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '参数错误' )); } $withdrawModel = M('rider_withdraw'); $record = $withdrawModel->where(array( 'id' => $id, 'rider_id' => $this->riderId ))->find(); if (!$record) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '提现记录不存在' )); } // 格式化数据 $data = array( 'id' => intval($record['id']), 'withdraw_no' => $record['withdraw_no'], 'amount' => floatval($record['amount']), 'fee' => floatval($record['fee']), 'actual_amount' => floatval($record['actual_amount']), 'withdraw_type' => intval($record['withdraw_type']), 'withdraw_type_text' => $this->getWithdrawTypeText($record['withdraw_type']), 'account_name' => $record['account_name'], 'account_number' => $this->maskAccountNumber($record['account_number']), 'bank_name' => $this->getDisplayBankName($record), 'status' => $this->mapStatusToFrontend($record['status']), 'status_text' => $this->getFrontendStatusText($record['status']), 'fail_reason' => $record['reject_reason'] ?: '', 'processed_at' => $this->getProcessedTime($record), 'created_at' => $record['add_time'] ); $this->ajaxReturn(array( 'code' => 1, 'msg' => '获取成功', 'data' => $data )); } catch (\Exception $e) { \Think\Log::write("获取提现详情失败:" . $e->getMessage(), 'ERROR'); $this->ajaxReturn(array( 'code' => 0, 'msg' => '获取失败,请稍后重试' )); } } /** * 提交提现申请 */ public function submitWithdraw() { // 检查骑手登录状态 if (!$this->riderId) { $this->ajaxReturn(array( 'code' => 0, 'msg' => '请先登录' )); } try { // 获取请求参数 $amount = $this->getRequestParam('amount', 0, 'floatval'); $withdrawMethod = $this->getRequestParam('withdraw_method', '', 'trim'); $bankCardId = $this->getRequestParam('bank_card_id', 0, 'intval'); $fee = $this->getRequestParam('fee', 0, 'floatval'); $actualAmount = $this->getRequestParam('actual_amount', 0, 'floatval'); // 参数验证 if ($amount <= 0) { $this->ajaxReturn(array('code' => 0, 'msg' => '请输入有效的提现金额')); } if (empty($withdrawMethod)) { $this->ajaxReturn(array('code' => 0, 'msg' => '请选择提现方式')); } if (!in_array($withdrawMethod, array('alipay', 'wechat', 'bank'))) { $this->ajaxReturn(array('code' => 0, 'msg' => '提现方式不支持')); } // 如果选择银行卡提现但没有指定银行卡ID,自动使用默认银行卡 if ($withdrawMethod === 'bank' && !$bankCardId) { $bankCardModel = M('rider_bank_card'); $defaultCard = $bankCardModel->where(array( 'rider_id' => $this->riderId, 'is_default' => 1, 'status' => 1 ))->find(); if (!$defaultCard) { // 如果没有默认银行卡,使用第一张银行卡 $defaultCard = $bankCardModel->where(array( 'rider_id' => $this->riderId, 'status' => 1 ))->order('add_time ASC')->find(); } if (!$defaultCard) { $this->ajaxReturn(array('code' => 0, 'msg' => '请先添加银行卡')); } $bankCardId = $defaultCard['id']; } // 获取提现配置 $configModel = M('rider_withdraw_config'); $merchantId = isset($this->riderInfo['merchant_id']) ? $this->riderInfo['merchant_id'] : 0; $config = $configModel->where(array( 'merchant_id' => $merchantId, 'status' => 1 ))->find(); if (!$config) { $config = $configModel->where(array( 'merchant_id' => 0, 'status' => 1 ))->find(); } if (!$config) { $this->ajaxReturn(array('code' => 0, 'msg' => '提现配置不存在')); } // 验证提现金额范围 if ($amount < $config['min_amount']) { $this->ajaxReturn(array('code' => 0, 'msg' => "最小提现金额为{$config['min_amount']}元")); } if ($amount > $config['max_amount']) { $this->ajaxReturn(array('code' => 0, 'msg' => "最大提现金额为{$config['max_amount']}元")); } // 检查今日提现次数 $today = date('Y-m-d'); $withdrawModel = M('rider_withdraw'); $todayCount = $withdrawModel->where(array( 'rider_id' => $this->riderId, 'add_time' => array('like', $today . '%') ))->count(); if ($todayCount >= $config['daily_limit']) { $this->ajaxReturn(array('code' => 0, 'msg' => "每日最多提现{$config['daily_limit']}次")); } // 检查余额 $riderModel = M('rider_info'); $riderInfo = $riderModel->where(array('id' => $this->riderId))->find(); if (!$riderInfo) { $this->ajaxReturn(array('code' => 0, 'msg' => '骑手信息不存在')); } if ($riderInfo['balance'] < $amount) { $this->ajaxReturn(array('code' => 0, 'msg' => '余额不足')); } // 获取提现账户信息 $accountName = ''; $accountNumber = ''; $bankName = ''; $withdrawType = 0; $bankCard = null; if ($withdrawMethod === 'bank') { $bankCardModel = M('rider_bank_card'); $bankCard = $bankCardModel->where(array( 'id' => $bankCardId, 'rider_id' => $this->riderId, 'status' => 1 ))->find(); if (!$bankCard) { $this->ajaxReturn(array('code' => 0, 'msg' => '银行卡不存在')); } $accountName = $bankCard['holder_name']; $accountNumber = $bankCard['card_number']; $bankName = $bankCard['bank_name']; $withdrawType = 1; } else { // 支付宝和微信使用骑手姓名和手机号 $accountName = $riderInfo['rider_name']; $accountNumber = $riderInfo['rider_phone']; $withdrawType = $withdrawMethod === 'alipay' ? 2 : 3; } // 计算手续费 $feeRateField = $withdrawMethod . '_fee_rate'; $calculatedFee = $amount * $config[$feeRateField]; $calculatedActualAmount = $amount - $calculatedFee; // 验证前端传递的费用计算是否正确 if (abs($fee - $calculatedFee) > 0.01 || abs($actualAmount - $calculatedActualAmount) > 0.01) { $this->ajaxReturn(array('code' => 0, 'msg' => '费用计算错误,请重新提交')); } // 开启事务 $withdrawModel->startTrans(); try { // 生成提现单号 $withdrawNo = 'WD' . date('YmdHis') . str_pad($this->riderId, 4, '0', STR_PAD_LEFT) . mt_rand(1000, 9999); // 插入提现记录 $withdrawData = array( 'rider_id' => $this->riderId, 'withdraw_no' => $withdrawNo, 'amount' => $amount, 'fee' => $calculatedFee, 'actual_amount' => $calculatedActualAmount, 'withdraw_type' => $withdrawType, 'account_name' => $accountName, 'account_number' => $accountNumber, 'bank_name' => $bankName, 'status' => 0, // 申请中 'add_time' => date('Y-m-d H:i:s') ); // 如果是银行卡提现,添加银行卡相关信息 if ($withdrawMethod === 'bank' && isset($bankCard)) { $withdrawData['bank_card_id'] = $bankCard['id']; $withdrawData['branch_name'] = $bankCard['branch_name']; } $withdrawId = $withdrawModel->add($withdrawData); if (!$withdrawId) { throw new \Exception('提现申请失败'); } // 获取当前余额 $currentRider = $riderModel->field('balance, frozen_balance')->find($this->riderId); $balanceBefore = floatval($currentRider['balance']); $frozenBefore = floatval($currentRider['frozen_balance']); // 扣除余额,增加冻结金额 $updateResult = $riderModel->where(array('id' => $this->riderId))->save(array( 'balance' => array('exp', 'balance - ' . $amount), 'frozen_balance' => array('exp', 'frozen_balance + ' . $amount) )); if ($updateResult === false) { throw new \Exception('余额更新失败'); } // 记录余额变动日志 $balanceLog = array( 'rider_id' => $this->riderId, 'type' => 'withdraw_apply', 'amount' => $amount, 'balance_before' => $balanceBefore, 'balance_after' => $balanceBefore - $amount, 'frozen_before' => $frozenBefore, 'frozen_after' => $frozenBefore + $amount, 'description' => '提现申请,从可用余额转入冻结余额', 'related_id' => $withdrawId, 'operator_id' => $this->riderId, 'add_time' => date('Y-m-d H:i:s') ); M('rider_balance_log')->add($balanceLog); $withdrawModel->commit(); // 记录日志 \Think\Log::write("骑手{$this->riderId}提交提现申请:金额{$amount}元,单号{$withdrawNo}", 'INFO'); $this->ajaxReturn(array( 'code' => 1, 'msg' => '提现申请提交成功', 'data' => array( 'withdraw_id' => intval($withdrawId), 'withdraw_no' => $withdrawNo, 'amount' => $amount, 'fee' => $calculatedFee, 'actual_amount' => $calculatedActualAmount, 'status' => 0, 'status_text' => '申请中', 'add_time' => $withdrawData['add_time'] ) )); } catch (\Exception $e) { $withdrawModel->rollback(); throw $e; } } catch (\Exception $e) { \Think\Log::write("提现申请失败:" . $e->getMessage(), 'ERROR'); $this->ajaxReturn(array( 'code' => 0, 'msg' => $e->getMessage() ?: '提现申请失败,请稍后重试' )); } } /** * 银行卡号脱敏 */ private function maskBankCard($bankCard) { if (strlen($bankCard) <= 8) { return $bankCard; } return substr($bankCard, 0, 4) . '****' . substr($bankCard, -4); } }