You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
432 lines
16 KiB
432 lines
16 KiB
<?php
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Exceptions\ApiException;
|
|
use App\Models\Coins;
|
|
use App\Models\OtcAccount;
|
|
use App\Models\OtcCoinlist;
|
|
use App\Models\OtcEntrust;
|
|
use App\Models\OtcOrder;
|
|
use App\Models\User;
|
|
use App\Models\UserPayment;
|
|
use App\Models\UserWallet;
|
|
use App\Services\ExchangeRateService\ExchangeRateService;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class OtcService
|
|
{
|
|
public function otcTicker()
|
|
{
|
|
return OtcCoinlist::query()->where('status', 1)->get();
|
|
}
|
|
|
|
public function tradingEntrusts($user, $params)
|
|
{
|
|
$builder = OtcEntrust::query()->with('user')->where(['coin_name' => $params['virtual_coin'], 'side' => $params['side']])->where('status', 1);
|
|
|
|
if (!empty($user)) {
|
|
$builder->where('user_id', '!=', $user['user_id']);
|
|
}
|
|
|
|
if ($params['pay_type'] != 'all') {
|
|
$builder->whereRaw('FIND_IN_SET(?,pay_type)', [$params['pay_type']]);
|
|
}
|
|
|
|
return $builder->paginate();
|
|
}
|
|
|
|
public function storeBuyEntrust($user, $params)
|
|
{
|
|
$pair = OtcCoinlist::query()->where('coin_name', $params['virtual_coin'])->first();
|
|
if (empty($pair)) throw new ApiException();
|
|
if (($can_store = $pair->can_store()) !== true) throw new ApiException($can_store);
|
|
|
|
// 检测收款方式
|
|
$payments = json_decode($params['pay_type'], true);
|
|
if (empty($payments)) throw new ApiException('收款方式不能为空');
|
|
foreach ($payments as $payment) {
|
|
$is_exist = UserPayment::query()
|
|
->where('user_id', $user['user_id'])
|
|
->where('pay_type', $payment)
|
|
->exists();
|
|
if (!$is_exist) throw new ApiException('未绑定该收款方式');
|
|
}
|
|
|
|
$amount = $params['amount'];
|
|
|
|
$overtime = $pair['max_register_time'];
|
|
if ($overtime == 0) {
|
|
$overed_time = 0;
|
|
} else {
|
|
$overed_time = Carbon::now()->addHours(intval($overtime))->toDateTimeString();
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
|
|
$entrust = OtcEntrust::query()->create([
|
|
'user_id' => $user['user_id'],
|
|
'side' => $params['side'],
|
|
'order_sn' => get_order_sn('otc'),
|
|
'coin_id' => $pair['coin_id'],
|
|
'coin_name' => $pair['coin_name'],
|
|
'min_num' => $params['min_num'] ?? null,
|
|
'max_num' => $params['max_num'] ?? null,
|
|
'pay_type' => implode(',', $payments),
|
|
'note' => $params['note'] ?? null,
|
|
'publish_time' => time(),
|
|
'price' => $params['price'],
|
|
'amount' => $amount,
|
|
'cur_amount' => $amount,
|
|
'overed_at' => $overed_time,
|
|
]);
|
|
|
|
DB::commit();
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
throw new ApiException($e->getMessage());
|
|
}
|
|
|
|
return $entrust;
|
|
}
|
|
|
|
public function storeSellEntrust($user, $params)
|
|
{
|
|
$pair = OtcCoinlist::query()->where('coin_name', $params['virtual_coin'])->first();
|
|
if (empty($pair)) throw new ApiException();
|
|
if (($can_store = $pair->can_store()) !== true) throw new ApiException($can_store);
|
|
|
|
// 检测收款方式
|
|
$payments = json_decode($params['pay_type'], true);
|
|
if (empty($payments)) throw new ApiException('收款方式不能为空');
|
|
foreach ($payments as $payment) {
|
|
$is_exist = UserPayment::query()
|
|
->where('user_id', $user['user_id'])
|
|
->where('pay_type', $payment)
|
|
->exists();
|
|
if (!$is_exist) throw new ApiException('未绑定该收款方式');
|
|
}
|
|
|
|
//法币账户
|
|
$account = OtcAccount::query()->where(['user_id' => $user['user_id'], 'coin_name' => $pair['coin_name']])->first();
|
|
if (empty($account)) throw new ApiException('账户类型错误');
|
|
$balance = $account->usable_balance;
|
|
$amount = $params['amount'];
|
|
if ($balance < $amount) throw new ApiException('余额不足');
|
|
|
|
$overtime = $pair['max_register_time'];
|
|
if ($overtime == 0) {
|
|
$overed_time = 0;
|
|
} else {
|
|
$overed_time = Carbon::now()->addHours(intval($overtime))->toDateTimeString();
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
|
|
$entrust = OtcEntrust::query()->create([
|
|
'user_id' => $user['user_id'],
|
|
'side' => $params['side'],
|
|
'order_sn' => get_order_sn('otc'),
|
|
'coin_id' => $pair['coin_id'],
|
|
'coin_name' => $pair['coin_name'],
|
|
'min_num' => $params['min_num'] ?? null,
|
|
'max_num' => $params['max_num'] ?? null,
|
|
'pay_type' => implode(',', json_decode($params['pay_type'], true)),
|
|
'note' => $params['note'] ?? null,
|
|
'publish_time' => time(),
|
|
'price' => $params['price'],
|
|
'amount' => $amount,
|
|
'cur_amount' => $amount,
|
|
'overed_at' => $overed_time,
|
|
]);
|
|
|
|
//扣除用户可用资产 冻结
|
|
$user->update_wallet_and_log($account['coin_id'], 'usable_balance', -$amount, UserWallet::otc_account, 'store_otc_sell_entrust');
|
|
$user->update_wallet_and_log($account['coin_id'], 'freeze_balance', $amount, UserWallet::otc_account, 'store_otc_sell_entrust');
|
|
|
|
DB::commit();
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
throw new ApiException($e->getMessage());
|
|
}
|
|
return $entrust;
|
|
}
|
|
|
|
public function storeOrder($user, $params)
|
|
{
|
|
$entrust = OtcEntrust::query()->find($params['entrust_id']);
|
|
if (empty($entrust)) throw new ApiException('委托不存在');
|
|
if ($user['user_id'] == $entrust['user_id']) throw new ApiException('不能和自己进行交易');
|
|
// if($entrust['side'] == $params['trans_type']) throw new ApiException('参数错误');
|
|
|
|
$trans_type = $entrust['side'] == 1 ? 2 : 1;
|
|
// 判断买卖双方的交易方式
|
|
$pay_type_arr = is_array($entrust['pay_type']) ? $entrust['pay_type'] : explode(',', $entrust['pay_type']);
|
|
if (!in_array($params['pay_type'], $pay_type_arr)) {
|
|
throw new ApiException('支付方式不匹配');
|
|
}
|
|
|
|
// 检测收款方式
|
|
$is_exist = UserPayment::query()
|
|
->where('user_id', $user['user_id'])
|
|
->where('pay_type', $params['pay_type'])
|
|
->exists();
|
|
if (!$is_exist) throw new ApiException('未绑定该收款方式');
|
|
|
|
|
|
$amount = $params['amount'];
|
|
if ($entrust['cur_amount'] < $amount) throw new ApiException('下单数量不得大于剩余数量');
|
|
if (!empty($entrust['min_num']) && $entrust['min_num'] != 0 && $entrust['min_num'] > $amount) throw new ApiException('下单数量不能小于最小限量');
|
|
if (!empty($entrust['max_num']) && $entrust['max_num'] != 0 && $entrust['max_num'] < $amount) throw new ApiException('下单数量不能大于最大限量');
|
|
|
|
$overtime = get_setting_value('otc_order_overed', 'otc', 15);
|
|
if ($overtime == 0) {
|
|
$overed_time = 0;
|
|
} else {
|
|
$overed_time = Carbon::now()->addMinutes(intval($overtime))->toDateTimeString();
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
$order_data = [
|
|
'trans_type' => $trans_type,
|
|
'order_sn' => get_order_sn('od'),
|
|
'user_id' => $user['user_id'],
|
|
'other_uid' => $entrust['user_id'],
|
|
'entrust_id' => $entrust['id'],
|
|
'coin_id' => $entrust['coin_id'],
|
|
'coin_name' => $entrust['coin_name'],
|
|
'amount' => $amount,
|
|
'pay_type' => $params['pay_type'],
|
|
'price' => $entrust['price'],
|
|
'money' => $params['amount'] * $entrust['price'],
|
|
'order_time' => time(),
|
|
'status' => OtcOrder::status_wait_pay,
|
|
'overed_at' => $overed_time,
|
|
];
|
|
$order = OtcOrder::query()->create($order_data);
|
|
|
|
if ($trans_type == 2) {
|
|
$user->update_wallet_and_log($order['coin_id'], 'usable_balance', -$amount, UserWallet::otc_account, 'store_otc_order');
|
|
$user->update_wallet_and_log($order['coin_id'], 'freeze_balance', $amount, UserWallet::otc_account, 'store_otc_order');
|
|
}
|
|
|
|
$entrust->update([
|
|
'cur_amount' => PriceCalculate($entrust['cur_amount'], '-', $amount, 6),
|
|
'lock_amount' => PriceCalculate($entrust['lock_amount'], '+', $amount, 6),
|
|
]);
|
|
|
|
DB::commit();
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
throw new ApiException($e->getMessage());
|
|
}
|
|
|
|
return $order;
|
|
}
|
|
|
|
public function myEntrusts($user_id, $params)
|
|
{
|
|
$builder = OtcEntrust::query()->where('user_id', $user_id);
|
|
|
|
if (!empty($params['side'])) {
|
|
$builder->where('side', $params['side']);
|
|
}
|
|
if (!empty($params['status'])) {
|
|
$builder->where('status', $params['status']);
|
|
}
|
|
|
|
return $builder->orderByDesc('id')->paginate();
|
|
}
|
|
|
|
public function myOrders($user_id, $params)
|
|
{
|
|
$builder = OtcOrder::query();
|
|
|
|
if (!empty($params['type'])) {
|
|
$type = $params['type'];
|
|
if ($type == 2) {
|
|
$builder->where(['trans_type' => 1, 'user_id' => $user_id]);
|
|
} elseif ($type == 1) {
|
|
$builder->where(['trans_type' => 2, 'user_id' => $user_id]);
|
|
} elseif ($type == 3) {
|
|
$builder->where(['trans_type' => 2, 'other_uid' => $user_id]);
|
|
} else {
|
|
$builder->where(['trans_type' => 1, 'other_uid' => $user_id]);
|
|
}
|
|
}
|
|
if (!empty($params['status'])) {
|
|
if ($params['status'] != 99) {
|
|
$builder->where('status', $params['status']);
|
|
}
|
|
}
|
|
|
|
return $builder->orderByDesc('id')->paginate();
|
|
}
|
|
|
|
public function orderDetail($user_id, $params)
|
|
{
|
|
return OtcOrder::query()->where('id', $params['order_id'])->where(function ($q) use ($user_id) {
|
|
$q->where('user_id', $user_id)->orWhere('other_uid', $user_id);
|
|
})->firstOrFail();
|
|
}
|
|
|
|
public function cancelEntrust($user_id, $params)
|
|
{
|
|
$entrust = OtcEntrust::query()->where(['user_id' => $user_id, 'id' => $params['entrust_id']])->firstOrFail();
|
|
if (!$entrust->canCancel()) {
|
|
throw new ApiException('当前委托不可撤销');
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
|
|
$entrust->update(['status' => OtcEntrust::status_canceled]);
|
|
|
|
if ($entrust['side'] == 2) {
|
|
// 退回剩余资金
|
|
$user = User::query()->findOrFail($user_id);
|
|
$user->update_wallet_and_log($entrust['coin_id'], 'usable_balance', $entrust['cur_amount'], UserWallet::otc_account, 'cancelOtcEntrust');
|
|
$user->update_wallet_and_log($entrust['coin_id'], 'freeze_balance', -$entrust['cur_amount'], UserWallet::otc_account, 'cancelOtcEntrust');
|
|
}
|
|
|
|
DB::commit();
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
throw $e;
|
|
}
|
|
|
|
return $entrust;
|
|
}
|
|
|
|
public function cancelOrder($user_id, $params)
|
|
{
|
|
$order = OtcOrder::query()->where(['id' => $params['order_id']])->firstOrFail();
|
|
if (!$order->canCancel()) {
|
|
throw new ApiException('当前订单不可撤销');
|
|
}
|
|
if ($order['user_id'] != $user_id && $order['other_uid'] != $user_id) {
|
|
throw new ApiException('非法操作');
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
|
|
$order->update(['status' => OtcEntrust::status_canceled]);
|
|
|
|
$amount = $order['amount'];
|
|
$entrust = $order->entrust;
|
|
$entrust->update([
|
|
'cur_amount' => PriceCalculate($entrust['cur_amount'], '+', $amount, 6),
|
|
'lock_amount' => PriceCalculate($entrust['lock_amount'], '-', $amount, 6),
|
|
]);
|
|
|
|
if ($order['trans_type'] == 2) {
|
|
$seller = $order->getSeller();
|
|
$seller->update_wallet_and_log($order['coin_id'], 'usable_balance', $amount, UserWallet::otc_account, 'cancel_otc_order');
|
|
$seller->update_wallet_and_log($order['coin_id'], 'freeze_balance', -$amount, UserWallet::otc_account, 'cancel_otc_order');
|
|
}
|
|
|
|
DB::commit();
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
throw $e;
|
|
}
|
|
|
|
return $order;
|
|
}
|
|
|
|
public function confirmPaidOrder($user_id, $params)
|
|
{
|
|
$order = OtcOrder::query()->where('id', $params['order_id'])->firstOrFail();
|
|
if (($checkRes = $order->canConfirmPaid()) !== true) throw new ApiException($checkRes);
|
|
|
|
if ($order['trans_type'] == 1) {
|
|
if ($user_id != $order['user_id']) throw new ApiException('非法操作');
|
|
} else {
|
|
if ($user_id != $order['other_uid']) throw new ApiException('非法操作');
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
|
|
$order->update(['status' => OtcOrder::status_wait_confirm, 'paid_img' => $params['paid_img'], 'pay_time' => time()]);
|
|
|
|
DB::commit();
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
throw $e;
|
|
}
|
|
|
|
return $order;
|
|
}
|
|
|
|
public function confirmOrder($user_id, $params)
|
|
{
|
|
$order = OtcOrder::query()->where('id', $params['order_id'])->firstOrFail();
|
|
if (($checkRes = $order->canConfirmOrder()) !== true) throw new ApiException($checkRes);
|
|
|
|
if ($order['trans_type'] == 1) {
|
|
if ($user_id != $order['other_uid']) throw new ApiException('非法操作');
|
|
} else {
|
|
if ($user_id != $order['user_id']) throw new ApiException('非法操作');
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
|
|
// 更新订单
|
|
$order->update(['status' => OtcOrder::status_completed, 'deal_time' => time()]);
|
|
|
|
// 更新委托
|
|
$entrust = $order->entrust;
|
|
$entrust->update(['lock_amount' => $entrust['lock_amount'] - $order['amount']]);
|
|
|
|
//买家入账
|
|
$buyer = $order->getBuyer();
|
|
$buyer->update_wallet_and_log($order['coin_id'], 'usable_balance', $order['amount'], UserWallet::otc_account, 'confirmOtcOrder');
|
|
|
|
// 卖家冻结金额减少
|
|
$seller = $order->getSeller();
|
|
$seller->update_wallet_and_log($order['coin_id'], 'freeze_balance', -$order['amount'], UserWallet::otc_account, 'confirmOtcOrder');
|
|
|
|
DB::commit();
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
throw $e;
|
|
}
|
|
|
|
return $order;
|
|
}
|
|
|
|
public function notConfirmOrder($user_id, $params)
|
|
{
|
|
$order = OtcOrder::query()->where('id', $params['order_id'])->firstOrFail();
|
|
if (($checkRes = $order->canConfirmOrder()) !== true) throw new ApiException($checkRes);
|
|
|
|
if ($order['trans_type'] == 1) {
|
|
if ($user_id != $order['other_uid']) throw new ApiException('非法操作');
|
|
} else {
|
|
if ($user_id != $order['user_id']) throw new ApiException('非法操作');
|
|
}
|
|
|
|
DB::beginTransaction();
|
|
try {
|
|
|
|
// 更新订单
|
|
$order->update(['status' => OtcOrder::status_appealing, 'appeal_status' => OtcOrder::appeal_status_wait, 'appeal_time' => time()]);
|
|
|
|
DB::commit();
|
|
} catch (\Exception $e) {
|
|
DB::rollBack();
|
|
throw $e;
|
|
}
|
|
|
|
return $order;
|
|
}
|
|
}
|
|
|