billDao = $billDao; $this->bankDao = $bankDao; $this->financialDao = $financialDao; $this->withdrawDao = $withdrawDao; } /** * 结算列表 * * @param $params * @return mixed */ public function bills($params) { // 站点信息 $channelId = Site::getCurrentChannelId(); $params['channel_id'] = $channelId; // 结算列表 $bills = $this->billDao->getBills($params); // 导出逻辑 $export = (int)getProp($params, 'export'); if ($export) { $this->exportBills($bills); } return $bills; } /** * 结算单订单明细 * * @param $date * @return mixed */ public function billOrders($date) { // 站点信息 $channelId = Site::getCurrentChannelId(); // 查询订单 return $this->billDao->billOrders($channelId, $date); } /** * 结算信息-汇总数据 * * @return array */ public function billsStat(): array { // 站点id $channelId = Site::getCurrentChannelId(); // 站点提现信息 $financialStat = $this->financialDao->getChannelFinancialStat($channelId); $enableWithdrawalAmount = getProp($financialStat, 'enable_withdrawal_amount_7day', 0.00); // t-7可提现余额 $accumulativeWithdrawalAmount = getProp($financialStat, 'accumulative_withdrawal_amount', 0.00); $latestWithdrawalAmount = getProp($financialStat, 'latest_withdrawal_amount', 0.00); $withdrawPendingAmount = getProp($financialStat, 'withdraw_pending_amount', 0.00); $latestWithdrawalTime = getProp($financialStat, 'latest_withdraw_time'); // 充值总额 // $totalRechargeAmount = $this->orderDao->getChannelRechargeAmount($channelId); // 充值总额(默认7日前数据) [$startDate, $endDate] = ['', date('Y-m-d', strtotime('-' . BaseConst::DATE_RANGE_DAYS . ' days'))]; $totalRechargeAmountBefore7Days = $this->billDao->getBillsRechargeAmount($channelId, $startDate, $endDate); // 月充值总额(前7日数据不显示) $monthRechargeAmountWithout7Days = 0.00; $startDate = date('Y-m-01'); if (date('j') > BaseConst::DATE_RANGE_DAYS) { $monthRechargeAmountWithout7Days = $this->billDao->getBillsRechargeAmount($channelId, $startDate, $endDate); } // 最近一笔提现中 $latestWithdrawLog = $this->withdrawDao->getLatestWithdrawingCash($channelId); $withdrawingAmount = getProp($latestWithdrawLog, 'amount', 0.00); // 结算总额 // $totalSettlementAmount = $accumulativeWithdrawalAmount + $enableWithdrawalAmount + $withdrawPendingAmount; // 站点统计 return [ 'total_recharge_amount' => 0.00, // floor($totalRechargeAmount * 100) / 100, // 充值总额 'total_recharge_amount_without_today' => 0.00, // 充值总额(不含当日) 'month_recharge_amount_without_today' => 0.00, // 月充值总额(不含当日) 'total_recharge_amount_without_7day' => floor($totalRechargeAmountBefore7Days * 100) / 100, // 充值总额(不含当日) 'month_recharge_amount_without_7day' => floor($monthRechargeAmountWithout7Days * 100) / 100, // 月充值总额(不含当日) 'enable_withdrawal_amount' => floor($enableWithdrawalAmount * 100) / 100, // 可提现总余额 'total_withdraw_amount' => floor($accumulativeWithdrawalAmount * 100) / 100, // 累计提现金额 'total_settlement_amount' => 0.00, // floor($totalSettlementAmount * 100) / 100, // 结算总额 'latest_withdrawal_amount' => floor($latestWithdrawalAmount * 100) / 100, // 最近一笔提现金额 'latest_withdraw_time' => $latestWithdrawalTime ?: '', // 最近一笔提现时间 'withdrawing_amount' => floor($withdrawingAmount * 100) / 100, // 提现中金额 ]; } /** * 提现列表 * * @param $params * @return mixed */ public function withdrawCashes($params) { // 站点id $channelId = Site::getCurrentChannelId(); $params['channel_id'] = $channelId; // 获取提现记录 return $this->withdrawDao->getWithdrawCashes($params); } /** * 提现 * * @param $params * @return array * @throws ApiException */ public function applyWithDraw($params): array { dLog('withdraw')->info('applyWithDraw-1', $params); // 相关参数 $channelId = Site::getCurrentChannelId(); $channelUserId = Site::getUid(); $amount = (float)getProp($params, 'amount'); $cardId = (int)getProp($params, 'card_id'); $isCompany = (int)getProp($params, 'is_company'); // 金额不小于1 if (!is_numeric($amount) || $amount < 1 || $cardId < 1) { Utils::throwError(ErrorConst::PARAM_ERROR_CODE); } // 单笔提现下限 if ($amount < FinanceConsts::WITH_DRAW_PRIVATE_MIN) { Utils::throwError(ErrorConst::WITHDRAW_CASH_AMOUNT); } // 单笔提现上限 if ($amount >= FinanceConsts::WITH_DRAW_PRIVATE_MAX) { Utils::throwError(ErrorConst::WITHDRAW_CASH_AMOUNT_MORE); } // 核对银行卡信息 $bankAccount = $this->bankDao->getValidCashAccountById($cardId); if (empty($bankAccount)) { Utils::throwError(ErrorConst::WITHDRAW_CASH_AMOUNT_ACCOUNT); } if ($bankAccount['is_company'] != $isCompany) { Utils::throwError(ErrorConst::WITHDRAW_CASH_AMOUNT_NOT_MATCH); } if ((int)$bankAccount['channel_user_id'] != $channelUserId) { Utils::throwError(ErrorConst::PRIVATE_ACCOUNT_NO_ACCESS); } dLog('withdraw')->info('applyWithDraw-2', compact('bankAccount')); // 判断当前渠道今日是否已有提现 $isWithDrawn = $this->checkChannelIsWithDrawByChannelId($channelId); if ($isWithDrawn) { Utils::throwError(ErrorConst::WITHDRAW_CASH_TODAY_USE); } dLog('withdraw')->info('applyWithDraw-3', compact('isWithDrawn')); // 判断账户可提现余额是否满足 $financialStat = $this->financialDao->getChannelFinancialStat($channelId); $enableWithDrawAmount = getProp($financialStat, 'enable_withdrawal_amount_7day'); // t-7可提现余额 dLog('withdraw')->info('applyWithDraw-4', compact('financialStat', 'enableWithDrawAmount')); // 余额判断(-1 小于, 0 等于, 1 大于, 第3个参数必须带上) if (bccomp($amount, $enableWithDrawAmount, 2) >= 1) { Utils::throwError(ErrorConst::WITHDRAW_NOT_ENOUGH); } // 执行提现 $result = $this->runWithDraw($channelId, $amount, $bankAccount, $financialStat); if (!$result) { Utils::throwError(ErrorConst::WITHDRAW_CASHES_FAILED); } return [ 'enable_amount' => $enableWithDrawAmount - $amount, 'withdraw_pending_amount' => $amount ]; } /** * 执行提现 * * @param $channelId * @param $amount * @param $bankAccount * @param FinancialStats $financialStat * @return bool */ private function runWithDraw($channelId, $amount, $bankAccount, FinancialStats $financialStat): bool { try { DB::beginTransaction(); // 更新渠道可提现金额 $financialStat->latest_withdrawal_amount = $amount; //最近提现金额 $financialStat->enable_withdrawal_amount -= $amount; //修改可提现金额 $financialStat->enable_withdrawal_amount_7day -= $amount; //修改可提现金额 $financialStat->withdraw_pending_amount += $amount; //修改提现中金额 $financialStat->latest_withdraw_time = date('Y-m-d H:i:s'); //修改提现时间 $financialStat->save(); // 创建提现单 $this->withdrawDao->addWithDrawCashes([ 'distribution_channel_id' => $channelId, 'distribution_channel_name' => Site::getCurrentChannelName(), 'tallage' => 0, 'amount' => $amount, 'status' => FinanceConsts::CHECK_PENDING, 'is_company' => (int)getProp($bankAccount, 'is_company'), 'account_bank' => getProp($bankAccount, 'account_bank'), 'bank_account' => getProp($bankAccount, 'card_number'), 'account_name' => getProp($bankAccount, 'account_name'), 'pay_merchant_company_id' => 1, // 目前只有一个公司主体 'bank' => getProp($bankAccount, 'bank'), ]); DB::commit(); // 添加渠道到今日提现队列中 FinanceCache::sAddChannelIdToWithDraw($channelId, date('Ymd')); return true; } catch (\Exception $e) { dLog('withdraw')->info('runWithDraw-exception', [$e->getMessage()]); $msg = sprintf('异常-站点 %s 提现 %s 元失败', $channelId, $amount); sendNotice($msg); DB::rollBack(); return false; } } /** * 校验渠道当日是否已有提现 * * @param $channelId * @return bool */ private function checkChannelIsWithDrawByChannelId($channelId): bool { // 缓存层判断 $ymd = date('Ymd'); if (FinanceCache::checkChannelIdIsInWithDraw($channelId, $ymd)) { return true; } // 数据库层判断 [$startTime, $endTime] = [date('Y-m-d'), date('Y-m-d') . ' 23:59:59']; $checkRes = $this->withdrawDao->checkChannelIdIsInWithDraw($channelId, $startTime, $endTime); if (!$checkRes) { return false; } // 更新缓存 FinanceCache::sAddChannelIdToWithDraw($channelId, $ymd); return true; } /** * 结算导出 * * @param $bills * @return void */ private function exportBills($bills) { // 设置超时时间 set_time_limit(0); // 组装下载数据 $data = []; if ($bills->count()) { foreach ($bills as $bill) { $data[] = [ getProp($bill, 'date'), getProp($bill, 'recharge_amount'), getProp($bill, 'rate') * 100 . '%', getProp($bill, 'service_amount'), getProp($bill, 'settlement_price'), ]; } } $title = ['结算日期', '充值金额', '佣金比例', '支付通道费', '结算后金额']; exportExcel($data, $title, '结算信息-' . date("YmdHis")); exit(); } }