Files
cycle_api/app/Models/Wallet/Platform/WalletPlatformBalanceTransactionModel.php
2024-03-25 06:15:36 +08:00

456 lines
21 KiB
PHP

<?php
namespace App\Models\Wallet\Platform;
use App\Bean\Model\Wallet\Platform\WalletPlatformBalanceTransactionBean;
use App\Bean\Queue\Wallet\QueueNotifyToPlatformBean;
use App\Bean\Queue\Wallet\QueueWalletPlatformWithdrawTransferBean;
use App\Cache\Table\Wallet\TableWalletSettingCache;
use App\Exceptions\ModelException;
use App\Jobs\Wallet\WalletNotifyToPlatformQueue;
use App\Jobs\Wallet\WalletPlatformWithdrawTransferQueue;
use App\Models\Wallet\Base\WalletBaseModel;
use App\Models\Wallet\Wallet\WalletAddressModel;
use App\Tools\Logs;
use App\Tools\Math;
use App\Tools\Times;
use App\Tools\Tools;
use Illuminate\Support\Facades\DB;
class WalletPlatformBalanceTransactionModel extends WalletBaseModel
{
protected $table = 'wallet_address_transaction';
protected $primaryKey = 'id';
protected $fillable = [
'id',
'sn',
'type',
'status',
'platform_id',
'currency_code',
'balance_id',
'block_transaction_id',
'bind_wallet_address_id',
'bind_wallet_address',
'from_wallet_address_id',
'from_wallet_address',
'to_wallet_address_id',
'to_wallet_address',
'uid',
'amount',
'fee_amount',
'before_total_amount',
'after_total_amount',
'remark',
'source_wallet_id',
'callback_time',
'callback_wallet_address_transaction_id',
'is_notify',
'created_at',
'updated_at',
];
const TYPE_RECHARGE_ADD = 1;
const TYPE_WITHDRAW_DEC = 2;
const TYPE_TRANSACTION_FEE_SUB = 10;
const TYPE_TRANSACTION_FEE_ADD = 11;
const TYPE_ADMIN_ADD = 12;
const TYPE_ADMIN_SUB = 13;
const TYPE = [
self::TYPE_RECHARGE_ADD => '充值',
self::TYPE_WITHDRAW_DEC => '提现',
self::TYPE_TRANSACTION_FEE_SUB => '交易手续费扣款',
self::TYPE_TRANSACTION_FEE_ADD => '交易手续费加款',
self::TYPE_ADMIN_ADD => '管理员加款',
self::TYPE_ADMIN_SUB => '管理员扣款',
];
const STATUS_WAIT = 1;
const STATUS_PROCESSING = 2;
const STATUS_SUCCESS = 3;
const STATUS_FAIL = 4;
const STATUS = [
self::STATUS_WAIT => '等待处理',
self::STATUS_PROCESSING => '处理中',
self::STATUS_SUCCESS => '成功',
self::STATUS_FAIL => '失败',
];
const IS_NOTIFY_WAIT = 1;
const IS_NOTIFY_PROCESS = 2;
const IS_NOTIFY_FINISH = 3;
const IS_NOTIFY_FAIL = 4;
const IS_NOTIFY = [
self::IS_NOTIFY_WAIT => '待处理',
self::IS_NOTIFY_PROCESS => '处理中',
self::IS_NOTIFY_FINISH => '处理完成',
self::IS_NOTIFY_FAIL => '处理失败',
];
//管理员扣款
function typeAdminSub($platform_id, $uid, $currency_code, $amount, $remark = null): bool
{
$bean = new WalletPlatformBalanceTransactionBean();
$bean->setPlatformId($platform_id);
$bean->setUid($uid);
$bean->setCurrencyCode($currency_code);
$bean->setAmount(-abs($amount)); //负数
$bean->setType(self::TYPE_ADMIN_SUB);
$bean->setStatus(self::STATUS_SUCCESS);
$bean->setRemark($remark);
try {
DB::beginTransaction();
//查询平台余额
$oWalletPlatformBalanceModel = new WalletPlatformBalanceModel();
$resBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalanceOrCreate($platform_id, $currency_code);
if (!$resBalanceModel) throw new ModelException('findPlatformBalanceOrCreate error');
$bean->setBalanceId($resBalanceModel->id);
$bean->setBeforeTotalAmount($resBalanceModel->total_amount);
$bean->setAfterTotalAmount(Math::bcSub($resBalanceModel->total_amount, abs($amount)));
//新增账变
$res = $this->addTransaction($bean);
if (!$res) throw new ModelException('addTransaction fail');
//变更余额
$res = $oWalletPlatformBalanceModel->subAvailableAmount($bean->getBalanceId(), abs($amount));
if (!$res) throw new ModelException('subAvailableAmount fail');
DB::commit();
Logs::SuccLog(__FUNCTION__, func_get_args());
return true;
} catch (\Exception $e) {
DB::rollBack();
Logs::ErrLog(__FUNCTION__ . ' rollBack', $e, func_get_args());
return false;
}
}
//管理员加款
function typeAdminAdd($platform_id, $uid, $currency_code, $amount, $remark = null): bool
{
$bean = new WalletPlatformBalanceTransactionBean();
$bean->setPlatformId($platform_id);
$bean->setUid($uid);
$bean->setCurrencyCode($currency_code);
$bean->setAmount(abs($amount)); //正数
$bean->setType(self::TYPE_ADMIN_ADD);
$bean->setStatus(self::STATUS_SUCCESS);
$bean->setRemark($remark);
try {
DB::beginTransaction();
//查询平台余额
$oWalletPlatformBalanceModel = new WalletPlatformBalanceModel();
$resBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalanceOrCreate($platform_id, $currency_code);
if (!$resBalanceModel) throw new ModelException('findPlatformBalanceOrCreate error');
$bean->setBalanceId($resBalanceModel->id);
$bean->setBeforeTotalAmount($resBalanceModel->total_amount);
$bean->setAfterTotalAmount(Math::bcAdd($resBalanceModel->total_amount, $bean->getAmount()));
//新增账变
$res = $this->addTransaction($bean);
if (!$res) throw new ModelException('addTransaction fail');
//变更余额
$res = $oWalletPlatformBalanceModel->addAvailableAmount($bean->getBalanceId(), $bean->getAmount());
if (!$res) throw new ModelException('addAvailableAmount fail');
DB::commit();
Logs::SuccLog(__FUNCTION__, func_get_args());
return true;
} catch (\Exception $e) {
DB::rollBack();
Logs::ErrLog(__FUNCTION__ . ' rollBack', $e, func_get_args());
return false;
}
}
//手续费扣除
function typeTransactionFeeSub($platform_id, $uid, $currency_code, $amount, $remark = null): bool
{
$bean = new WalletPlatformBalanceTransactionBean();
$bean->setPlatformId($platform_id);
$bean->setUid($uid);
$bean->setCurrencyCode($currency_code);
$bean->setAmount(-abs($amount)); //负数
$bean->setType(self::TYPE_TRANSACTION_FEE_SUB);
$bean->setStatus(self::STATUS_SUCCESS);
$bean->setRemark($remark);
try {
DB::beginTransaction();
//查询平台余额
$oWalletPlatformBalanceModel = new WalletPlatformBalanceModel();
$resBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalanceOrCreate($platform_id, $currency_code);
if (!$resBalanceModel) throw new ModelException('findPlatformBalanceOrCreate error');
$bean->setBalanceId($resBalanceModel->id);
$bean->setBeforeTotalAmount($resBalanceModel->total_amount);
$bean->setAfterTotalAmount(Math::bcSub($resBalanceModel->total_amount, abs($amount)));
//新增账变
$res = $this->addTransaction($bean);
if (!$res) throw new ModelException('addTransaction fail');
//变更余额
$res = $oWalletPlatformBalanceModel->subAvailableAmount($bean->getBalanceId(), abs($amount));
if (!$res) throw new ModelException('subAvailableAmount fail');
DB::commit();
Logs::SuccLog(__FUNCTION__, func_get_args());
return true;
} catch (\Exception $e) {
DB::rollBack();
Logs::ErrLog(__FUNCTION__ . ' rollBack', $e, func_get_args());
return false;
}
}
//手续费增加
function typeTransactionFeeAdd($platform_id, $uid, $currency_code, $amount, $remark = null): bool
{
$bean = new WalletPlatformBalanceTransactionBean();
$bean->setPlatformId($platform_id);
$bean->setUid($uid);
$bean->setCurrencyCode($currency_code);
$bean->setAmount(abs($amount)); //正数
$bean->setType(self::TYPE_TRANSACTION_FEE_ADD);
$bean->setStatus(self::STATUS_SUCCESS);
$bean->setRemark($remark);
try {
DB::beginTransaction();
//查询平台余额
$oWalletPlatformBalanceModel = new WalletPlatformBalanceModel();
$resBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalanceOrCreate($platform_id, $currency_code);
if (!$resBalanceModel) throw new ModelException('findPlatformBalanceOrCreate error');
$bean->setBalanceId($resBalanceModel->id);
$bean->setBeforeTotalAmount($resBalanceModel->total_amount);
$bean->setAfterTotalAmount(Math::bcAdd($resBalanceModel->total_amount, $bean->getAmount()));
//新增账变
$res = $this->addTransaction($bean);
if (!$res) throw new ModelException('addTransaction fail');
//变更余额
$res = $oWalletPlatformBalanceModel->addAvailableAmount($bean->getBalanceId(), $bean->getAmount());
if (!$res) throw new ModelException('addAvailableAmount fail');
DB::commit();
Logs::SuccLog(__FUNCTION__, func_get_args());
return true;
} catch (\Exception $e) {
DB::rollBack();
Logs::ErrLog(__FUNCTION__ . ' rollBack', $e, func_get_args());
return false;
}
}
//充值
function typeRecharge($platform_id, $uid, $currency_code, $amount, $to_wallet_address_id = null, $tid = null, $callback_wallet_address_transaction_id = null): bool
{
$bean = new WalletPlatformBalanceTransactionBean();
$bean->setBlockTransactionId($tid);
$bean->setPlatformId($platform_id);
$bean->setUid($uid);
$bean->setCurrencyCode($currency_code);
$bean->setAmount(abs($amount)); //正数
$bean->setType(self::TYPE_RECHARGE_ADD);
$bean->setStatus(self::STATUS_SUCCESS);
$bean->setCallbackWalletAddressTransactionId($callback_wallet_address_transaction_id);
$bean->setToWalletAddressId($to_wallet_address_id);
try {
DB::beginTransaction();
//检测钱包地址和当前绑定地址是否一致
$oWalletPlatformBindModel = new WalletPlatformBindModel();
$resBindModel = $oWalletPlatformBindModel->findBind($platform_id, $uid, $currency_code);
if (!$resBindModel) throw new ModelException('WalletPlatformBindModel findBind error');
//查找钱包地址
$oWalletAddressModel = new WalletAddressModel();
$resWalletAddressModel = $oWalletAddressModel->findItem($resBindModel->wallet_address_id);
if (!$resWalletAddressModel) throw new ModelException('WalletAddressModel findItem error');
//检测异步到账地址是否和绑定地址一致
if (!empty($wallet_address_id)) {
if ($resWalletAddressModel->id != $to_wallet_address_id) throw new ModelException('wallet_address_id not same');
}
$bean->setBindWalletAddressId($resWalletAddressModel->id);
$bean->setBindWalletAddress($resWalletAddressModel->address_base58);
//查询平台余额
$oWalletPlatformBalanceModel = new WalletPlatformBalanceModel();
$resBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalanceOrCreate($platform_id, $currency_code);
if (!$resBalanceModel) throw new ModelException('findPlatformBalanceOrCreate error');
$bean->setBalanceId($resBalanceModel->id);
$bean->setBeforeTotalAmount($resBalanceModel->total_amount);
$bean->setAfterTotalAmount(Math::bcAdd($resBalanceModel->total_amount, $bean->getAmount()));
//新增账变
$res = $this->addTransaction($bean);
if (!$res) throw new ModelException('addTransaction fail');
//变更余额
$res = $oWalletPlatformBalanceModel->addAvailableAmount($bean->getBalanceId(), $bean->getAmount());
if (!$res) throw new ModelException('addAvailableAmount fail');
DB::commit();
Logs::SuccLog(__FUNCTION__, func_get_args());
return true;
} catch (\Exception $e) {
DB::rollBack();
Logs::ErrLog(__FUNCTION__ . ' rollBack', $e);
return false;
}
}
//发起提现
function typeWithdrawFirst($platform_id, $uid, $currency_code, $amount, $to_address = null): bool
{
$bean = new WalletPlatformBalanceTransactionBean();
$bean->setPlatformId($platform_id);
$bean->setUid($uid);
$bean->setCurrencyCode($currency_code);
$bean->setAmount(-abs($amount)); //负数
$bean->setType(self::TYPE_WITHDRAW_DEC);
$bean->setStatus(self::STATUS_WAIT);
$bean->setToWalletAddress($to_address);
try {
DB::beginTransaction();
//获取提现手续费
$oTableWalletSettingCache = new TableWalletSettingCache();
$withdraw_fee = $oTableWalletSettingCache->getWithdrawFeeAmount();
//查询平台余额
$oWalletPlatformBalanceModel = new WalletPlatformBalanceModel();
$resBalanceModel = $oWalletPlatformBalanceModel->findPlatformBalanceOrCreate($platform_id, $currency_code);
if (!$resBalanceModel) throw new ModelException('findPlatformBalanceOrCreate error');
//检查余额是否足够
if (Math::bcComp(Math::bcSub($resBalanceModel->available_amount, $withdraw_fee), abs($bean->getAmount())) == -1) throw new ModelException('available_amount not enough');
$bean->setBalanceId($resBalanceModel->id);
$bean->setBeforeTotalAmount($resBalanceModel->total_amount);
$bean->setAfterTotalAmount(Math::bcSub($resBalanceModel->total_amount, $bean->getAmount()));
$bean->setFeeAmount($withdraw_fee);
//查询当前绑定地址
$oWalletPlatformBindModel = new WalletPlatformBindModel();
$resBindModel = $oWalletPlatformBindModel->findBind($platform_id, $uid, $currency_code);
if (!$resBindModel) throw new ModelException('findBind error');
//查找钱包地址
$oWalletAddressModel = new WalletAddressModel();
$resWalletAddressModel = $oWalletAddressModel->findItem($resBindModel->wallet_address_id);
if (!$resWalletAddressModel) throw new ModelException('WalletAddressModel findItem error');
$bean->setBindWalletAddressId($resWalletAddressModel->id);
$bean->setBindWalletAddress($resWalletAddressModel->address_base58);
//新增账变
$res = $this->addTransaction($bean);
if (!$res) throw new ModelException('addTransaction fail');
//先扣除手续费
$res = $this->typeTransactionFeeSub($platform_id, $uid, $currency_code, $withdraw_fee, $res->id .'withdraw fee');
if (!$res) throw new ModelException('withdraw fee fail');
//变更余额
//先冻结,等回调成功再扣减
$res = $oWalletPlatformBalanceModel->frozenAmount($bean->getBalanceId(), $bean->getAmount());
if (!$res) throw new ModelException('frozenAmount fail');
//投递到提现处理队列
$oQueueWalletPlatformWithdrawBean = new QueueWalletPlatformWithdrawTransferBean();
$oQueueWalletPlatformWithdrawBean->setTransactionId($bean->getId());
WalletPlatformWithdrawTransferQueue::putToQueue($oQueueWalletPlatformWithdrawBean);
DB::commit();
Logs::SuccLog(__FUNCTION__, func_get_args());
return true;
} catch (\Exception $e) {
DB::rollBack();
Logs::ErrLog(__FUNCTION__ . 'rollBack', $e);
return false;
}
}
//提现回调
function typeWithdrawSecondCallback($id, $status, $remark = null, $callback_wallet_address_transaction_id = null): bool
{
try {
DB::beginTransaction();
//获取账变
$resModel = $this->findItem($id);
if (!$resModel) throw new ModelException('findItem error');
if ($resModel->status != self::STATUS_PROCESSING) throw new ModelException('status error');
//查询平台余额
$oWalletPlatformBalanceModel = new WalletPlatformBalanceModel();
$resBalanceModel = $oWalletPlatformBalanceModel->findItem($resModel->balance_id);
if (!$resBalanceModel) throw new ModelException('WalletPlatformBalanceModel findItem error');
if ($status == self::STATUS_SUCCESS) {
//变更余额
$res = $oWalletPlatformBalanceModel->subFrozenAmount($resModel->balance_id, $resModel->amount);
if (!$res) throw new ModelException('subFrozenAmount error');
//更新账变状态
$updateItem = [
'id' => $id,
'status' => self::STATUS_SUCCESS,
'remark' => $remark,
'updated_at' => Times::getNowDateTime(),
'callback_time' => Times::getNowDateTime(),
'after_total_amount' => Math::bcAdd($resBalanceModel->total_amount, $resModel->amount),
'before_total_amount' => $resBalanceModel->total_amount,
'callback_wallet_address_transaction_id' => $callback_wallet_address_transaction_id,
];
if (!empty($remark)) $updateItem['remark'] = $remark;
$res = $this->updateItem($updateItem);
if (!$res) throw new ModelException('updateItem error');
//投递到通知平台队列
$oQueueNotifyToPlatformBean = new QueueNotifyToPlatformBean();
$oQueueNotifyToPlatformBean->setType(QueueNotifyToPlatformBean::TYPE_WITHDRAW);
$oQueueNotifyToPlatformBean->setPlatformId($resModel->platform_id);
$oQueueNotifyToPlatformBean->setCurrencyCode($resModel->currency_code);
$oQueueNotifyToPlatformBean->setAmount($resModel->amount);
$oQueueNotifyToPlatformBean->setStatus(self::STATUS_SUCCESS);
$oQueueNotifyToPlatformBean->setSn($resModel->sn);
WalletNotifyToPlatformQueue::putToQueue($oQueueNotifyToPlatformBean);
} else {
//变更余额
$res = $oWalletPlatformBalanceModel->unFrozenAmount($resModel->balance_id, $resModel->amount);
if (!$res) throw new ModelException('unFrozenAmount error');
//更新账变状态
$updateItem = [
'id' => $id,
'status' => self::STATUS_FAIL,
'updated_at' => Times::getNowDateTime(),
'callback_time' => Times::getNowDateTime(),
'callback_wallet_address_transaction_id' => $callback_wallet_address_transaction_id,
];
if (!empty($remark)) $updateItem['remark'] = $remark;
$res = $this->updateItem($updateItem);
if (!$res) throw new ModelException('updateItem error');
//返还手续费
$res = $this->typeTransactionFeeAdd($resModel->platform_id, $resModel->uid, $resModel->currency_code, $resModel->fee_amount, $resModel->id . 'withdraw fail return fee');
if (!$res) throw new ModelException('withdraw fail return fee fail');
}
DB::commit();
Logs::SuccLog(__FUNCTION__, func_get_args());
return true;
} catch (\Exception $e) {
DB::rollBack();
Logs::ErrLog(__FUNCTION__ . ' rollBack', $e, func_get_args());
return false;
}
}
function addTransaction(WalletPlatformBalanceTransactionBean &$bean): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Builder|bool
{
if (!$bean->getPlatformId()) throw new ModelException('addTransaction getPlatformId is required');
if (!$bean->getCurrencyCode()) throw new ModelException('addTransaction getCurrencyCode is required');
if (!$bean->getType()) throw new ModelException('addTransaction getType is required');
if (!$bean->getStatus()) throw new ModelException('addTransaction getStatus is required');
if (!$bean->getBalanceId()) throw new ModelException('addTransaction getBalanceId is required');
if (!$bean->getBindWalletAddressId()) throw new ModelException('addTransaction getBindWalletAddressId is required');
if (empty($bean->getSn())) $bean->setSn(Tools::genUuid());
$bean->setCreatedAt(Times::getNowDateTime());
return $this->addItem($bean->toArrayNotNull());
}
function findByBlockTransactionId(string $tid, $type): \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|\Illuminate\Database\Eloquent\Builder|array|null
{
if (!in_array($type, [self::TYPE_RECHARGE_ADD, self::TYPE_WITHDRAW_DEC])) throw new ModelException('type error');
return $this->findItemByWhere(['block_transaction_id' => $tid, 'type' => $type]);
}
}