文件
mashangyoujiu/Application/RiderApi/Controller/IncomeController.class.php

1319 行
49 KiB
PHP

<?php
namespace RiderApi\Controller;
/**
* 骑手端收入控制器
*/
class IncomeController extends BaseController {
/**
* 获取收入概览
*/
public function getIncomeOverview() {
try {
$riderId = $this->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);
}
}