// +---------------------------------------------------------------------- namespace App\Services\Common; use App\Models\CapitalLogModel; use App\Models\MemberModel; use App\Models\TradeOrderModel; use App\Services\Api\MemberPaymentService; use App\Services\BaseService; use App\Services\ConfigService; /** * 用户交易订单-服务类 * Class TradeOrderService * @package App\Services\Common */ class TradeOrderService extends BaseService { // 静态对象 protected static $instance = null; /** * 构造函数 * @since 2020/11/10 * TradeOrderService constructor. */ public function __construct() { $this->model = new TradeOrderModel(); $this->memberModel = new MemberModel(); $this->capitalModel = new CapitalLogModel(); } /** * 静态入口 * @return static|null */ public static function make() { if (!self::$instance) { self::$instance = (new static()); } return self::$instance; } /** * 订单列表 * @param $params * @param int $pageSize * @return array */ public function getDataList($params, $pageSize = 15) { $list = $this->model->from('trade_order as a') ->leftJoin('member as b', 'b.id', '=', 'a.user_id') ->where(function ($query) use ($params) { $query->where(['a.mark' => 1])->where('a.status', '>', 0); $orderNo = isset($params['order_no']) && $params['order_no'] ? trim($params['order_no']) : ''; if ($orderNo) { $query->where('a.order_no', 'like', "%{$orderNo}%"); } $type = isset($params['type']) ? intval($params['type']) : 0; if ($type > 0) { $query->where(['a.type' => $type]); } // 日期 $date = isset($params['date']) ? $params['date'] : ''; if ($date) { } $exceptionStatus = isset($params['exception_status']) ? intval($params['exception_status']) : 0; if ($exceptionStatus > 0) { $query->where(['a.exception_status' => $exceptionStatus]); } $userId = isset($params['user_id']) ? $params['user_id'] : 0; if ($userId > 0) { $query->where('a.user_id', $userId); } $businessId = isset($params['business_id']) ? $params['business_id'] : 0; if ($businessId > 0) { $query->where('a.business_id', $businessId); } }) ->select(['a.*', 'b.username']) ->paginate($pageSize > 0 ? $pageSize : 9999999); $list = $list ? $list->toArray() : []; if ($list) { $overTime = ConfigService::make()->getConfigByCode('trade_order_overtime'); $overTime = $overTime ? $overTime : 0; foreach ($list['data'] as &$item) { $item['idcardData'] = $item['idcard_data'] ? json_decode($item['idcard_data'], true) : []; $item['paymentData'] = $item['payment_data'] ? json_decode($item['payment_data'], true) : []; $item['create_time_text'] = $item['create_time'] ? datetime($item['create_time'], 'Y-m-d H:i') : ''; $item['time_text'] = $item['create_time'] ? datetime($item['create_time'], 'H:i') : ''; $item['pay_time_text'] = $item['pay_time'] ? datetime($item['pay_time'], 'Y-m-d H:i') : ''; $item['username_text'] = $item['username'] ? format_account($item['username']) : ''; $overTime = max(0, intval($item['create_time']) + $overTime * 60 - time()); $item['overtime_text'] = in_array($item['status'], [1, 2]) && $overTime ? date('H:i', $overTime) : ''; } } return [ 'pageSize' => $pageSize, 'total' => isset($list['total']) ? $list['total'] : 0, 'list' => isset($list['data']) ? $list['data'] : [] ]; } /** * 成交交易笔数 * @param int $type 类型:1-买单,2-卖单 * @param int $bsid 承兑商用户ID * @param int $status 状态:4-成交 * @return mixed */ public function getCompleteCount($type = 1, $bsid = 0, $status = 4) { return (int)$this->model->where(['type' => $type, 'status' => $status, 'mark' => 1]) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } })->count('id'); } /** * 成交交易金额 * @param int $type 类型:1-买单,2-卖单 * @param int $bsid 承兑商用户ID * @param int $status 状态:4-成交 * @param string $field 统计字段:total-总金额(默认),num-交易USDT数量 * @return mixed */ public function getCompleteTotal($type = 1, $bsid = 0, $status = 4, $field = 'total') { $total = $this->model->where(['type' => $type, 'status' => $status, 'mark' => 1]) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } })->sum($field); return moneyFormat($total, 2); } /** * 今日成交交易金额 * @param int $type 类型:1-买单,2-卖单 * @param int $bsid 承兑商用户ID * @param int $status 状态:4-成交 * @param string $field 统计字段:total-总金额(默认),num-交易USDT数量 * @return mixed */ public function getCompleteTotalByDay($type = 1, $bsid = 0, $status = 4, $field = 'total') { $total = $this->model->where(['type' => $type, 'status' => $status, 'mark' => 1]) ->where('pay_time', '>=', strtotime(date('Y-m-d'))) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } })->sum($field); return moneyFormat($total, 6); } /** * 获取某个时间段的买卖交易量比 * @param int $type 状态类型:1-成交的 * @param int $bsid 承兑商用户ID * @param int $time 时间/分钟,默认15分钟 * @return array */ public function getCountRateByTime($type = 1, $bsid = 0, $time = 15) { $where = ['mark' => 1]; if ($type == 1) { $where['status'] = 4; } $buyCount = $this->model->where(['type' => 1])->where($where) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } }) ->where('create_time', '>=', time() - $time * 60) ->count('id'); $sellCount = $this->model->where(['type' => 2])->where($where) ->where(function ($query) use ($bsid) { if ($bsid > 0) { $query->where('business_id', '=', $bsid); } }) ->where('create_time', '>=', time() - $time * 60) ->count('id'); $rate = ($buyCount || $sellCount) > 0 ? moneyFormat($buyCount / ($buyCount + $sellCount)) * 100 : 0; return ['buy' => $buyCount, 'sell' => $sellCount, 'buy_rate' => $rate, 'sell_rate' => ($sellCount > 0 ? 100 - $rate : 0)]; } /** * 获取未支付或处理的订单数 * @param $userId * @param int $type * @return mixed */ public function checkOrderNoCatch($userId, $type = 1) { return $this->model->where(['user_id' => $userId, 'type' => $type, 'mark' => 1])->whereIn('status', [1, 2])->count('id'); } /** * 客户买入 * @param $userId * @param $params * @return false|int|number */ public function buy($userId, $params) { $num = isset($params['num']) ? floatval($params['num']) : 0; $numType = isset($params['num_type']) ? intval($params['num_type']) : 1; if ($userId <= 0) { $this->error = '1013'; return false; } // 验证参数 $config = ConfigService::make()->getConfigOptionByGroup(5); $tradeOpen = isset($config['trade_usdt_open']) ? $config['trade_usdt_open'] : 0; $tradeMinNum = isset($config['trade_min_num']) ? $config['trade_min_num'] : 0; $tradeMaxNum = isset($config['trade_max_num']) ? $config['trade_max_num'] : 0; $trademinMoney = isset($config['trade_min_money']) ? $config['trade_min_money'] : 0; $tradeMaxMoney = isset($config['trade_max_money']) ? $config['trade_max_money'] : 0; $tradePrice = isset($config['usdt_buy_price']) ? $config['usdt_buy_price'] : 0; $tradeLimitNum = isset($config['trade_no_catch']) ? $config['trade_no_catch'] : 0; // 是否开启交易 if ($tradeOpen != 1) { $this->error = '1013'; return false; } if ($tradePrice <= 0) { $this->error = '3002'; return false; } // 验证数量或金额 $total = 0; if ($numType == 1) { if ($num < $tradeMinNum || $num > $tradeMaxNum) { $this->error = '3003'; return false; } $total = moneyFormat($num * $tradePrice, 6); } else { if ($num < $trademinMoney || $num > $tradeMaxMoney) { $this->error = '3003'; return false; } $total = moneyFormat($num, 6); $num = moneyFormat($num * $tradePrice, 6); } // 用户信息 $userInfo = MemberService::make()->getInfo($userId); $status = isset($userInfo['status']) ? $userInfo['status'] : 0; if ($status != 1) { $this->error = '2009'; return false; } // 匹配交易商家 $businessInfo = \App\Services\Api\MemberService::make()->getTradeMember($num, 1, $userId); if (empty($businessInfo)) { $this->error = '3004'; return false; } $noCatchOrder = $this->checkOrderNoCatch($userId, 1); if ($tradeLimitNum > 0 && $noCatchOrder >= $tradeLimitNum) { $this->error = lang(3005, ['num' => $tradeLimitNum]); return false; } $data = [ 'user_id' => $userId, 'business_id' => isset($businessInfo['id']) ? $businessInfo['id'] : 0, 'order_no' => get_order_num('OT'), 'type' => 1, 'pay_type' => isset($params['pay_type']) ? floatval($params['pay_type']) : 1, 'price' => $tradePrice, 'num' => $num, 'total' => $total, 'create_time' => time(), 'update_time' => time(), 'status' => 1, 'mark' => 1, ]; return $this->model->edit($data); } /** * 客户卖出 * @param $userId * @param $params * @return false|int|number */ public function sell($userId, $params) { $num = isset($params['num']) ? floatval($params['num']) : 0; if ($userId <= 0) { $this->error = '1013'; return false; } // 验证参数 $config = ConfigService::make()->getConfigOptionByGroup(5); $tradeOpen = isset($config['trade_usdt_open']) ? $config['trade_usdt_open'] : 0; $tradeMinNum = isset($config['trade_min']) ? $config['trade_min'] : 0; $tradeMaxNum = isset($config['trade_max']) ? $config['trade_max'] : 0; $tradePrice = isset($config['usdt_sell_price']) ? $config['usdt_sell_price'] : 0; $tradeLimitNum = isset($config['trade_no_catch']) ? $config['trade_no_catch'] : 0; // 是否开启交易 if ($tradeOpen != 1) { $this->error = '1013'; return false; } if ($tradePrice <= 0) { $this->error = '3002'; return false; } // 验证数量或金额 $total = moneyFormat($num * $tradePrice, 6); if ($num < $tradeMinNum || ($tradeMaxNum && $num > $tradeMaxNum)) { $this->error = '3003'; return false; } // 用户信息 $userInfo = MemberService::make()->getInfo($userId); $status = isset($userInfo['status']) ? $userInfo['status'] : 0; $usdtNum = isset($userInfo['usdt_num']) ? $userInfo['usdt_num'] : 0; if ($status != 1) { $this->error = '2009'; return false; } if ($usdtNum < $num) { $this->error = '3011'; return false; } // 匹配交易商家 $businessInfo = \App\Services\Api\MemberService::make()->getTradeMember($num, 2, $userId); if (empty($businessInfo)) { $this->error = '3004'; return false; } // 未处理订单 $noCatchOrder = $this->checkOrderNoCatch($userId, 2); if ($tradeLimitNum > 0 && $noCatchOrder >= $tradeLimitNum) { $this->error = lang(3005, ['num' => $tradeLimitNum]); return false; } // 收款方式 $paymentId = isset($params['payment_id']) ? $params['payment_id'] : 0; $paymentInfo = MemberPaymentService::make()->getInfo($paymentId); if (empty($paymentInfo)) { $this->error = '3010'; return false; } $paymentData = [ 'payment_id' => $paymentInfo['id'], 'type' => $paymentInfo['type'], 'logo' => $paymentInfo['logo'] ? get_image_url($paymentInfo['logo']) : '', 'real_name' => $paymentInfo['real_name'], 'bank_name' => $paymentInfo['bank_name'], 'bank_card' => $paymentInfo['bank_card'], 'branch_name' => $paymentInfo['branch_name'], 'qrcode' => $paymentInfo['qrcode'] ? get_image_url($paymentInfo['qrcode']) : '', 'account' => $paymentInfo['account'], ]; $data = [ 'user_id' => $userId, 'business_id' => isset($businessInfo['id']) ? $businessInfo['id'] : 0, 'order_no' => get_order_num('OT'), 'type' => 2, 'pay_type' => isset($params['pay_type']) ? floatval($params['pay_type']) : 1, 'price' => $tradePrice, 'num' => $num, 'payment_id' => $paymentId, 'payment_data' => json_encode($paymentData, 256), 'total' => $total, 'create_time' => time(), 'status' => 1, 'mark' => 1, ]; $this->model->startTrans(); if (!$order = $this->model->edit($data)) { $this->error = '3012'; $this->model->rollBack(); return false; } // 扣除币 if (!$this->memberModel->where(['id' => $userId])->decrement('usdt_num', $num)) { $this->error = '3013'; $this->model->rollBack(); return false; } // 账户明细 $data = [ 'order_no' => $data['order_no'], 'user_id' => $userId, 'type' => 1, 'change_type' => 2, 'num' => $num, 'total' => $total, 'create_time' => time(), 'remark' => '', 'status' => 1, 'mark' => 1, ]; if (!$this->capitalModel->edit($data)) { $this->error = '3014'; $this->model->rollBack(); return false; } $this->model->commit(); return $order; } public function catchInvalidOrder() { $overtime = ConfigService::make()->getConfigByCode('trade_order_overtime'); $cancelTime = ConfigService::make()->getConfigByCode('trade_order_cancel'); $catchNum = ConfigService::make()->getConfigByCode('trade_order_catch_num'); $catchNum = $catchNum>0? $catchNum : 200; // 处理超时订单 if ($overtime > 0) { $this->model->where(['mark' => 1]) ->where('status', '<=', 2) ->where('create_time', '<=', time() - $overtime * 60) ->update(['status' => 7, 'catch_at' => time()]); } if ($cancelTime <= 0) { $this->error = '1023'; return false; } $this->model->where(function ($query) use ($cancelTime) { // 已更新为超时的订单 $query->where(['mark' => 1, 'status' => 7]) ->where('catch_at', '<=', time() - $cancelTime * 60); }) ->orWhere(function ($query) use ($cancelTime, $overtime) { $query->where('mark', '=', 1) ->where('status', '<=', 2) ->where('create_time', '<=', time() - ($cancelTime + $overtime) * 60); }) ->select(['id', 'user_id', 'business_id', 'type', 'num']) ->get() ->limit(500) ->each(function ($item, $k) { // $type = isset($item['type'])? $item['type'] : 0; if($type == 2){ } }); } }