| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- <?php
- namespace App\Services\Simulation;
- use App\Constants\HttpStatus;
- use App\Models\PaymentOrder;
- use App\Models\Wallet;
- use App\Services\BalanceLogService;
- use App\Services\PaymentOrderService;
- use App\Services\WageringService;
- use App\Services\WalletFreezeService;
- use App\Services\WithdrawFundsService;
- use Exception;
- use Illuminate\Support\Facades\DB;
- /**
- * 模拟会员的资金:订单、流水、冻结都写真实业务表并走真实入账代码,只是不向任何三方发请求。
- * 充值有两条路:公司入款(人工充值单审核通过)和三方充值(代收单按成功回调入账);提现走人工提现。
- */
- final class SimulationFundService
- {
- public const CHANNEL = 'simulation';
- /** @param string $channel company|third @param string $result success|pending|fail */
- public static function recharge($memberId, $amount, string $channel = 'third', string $result = 'success'): PaymentOrder
- {
- SimulationSettings::assertEnabled();
- $user = SimulationSettings::member($memberId);
- $amount = SimulationSettings::assertAmount($amount);
- $type = $channel === 'company' ? PaymentOrderService::TYPE_SELF_PAY : PaymentOrderService::TYPE_PAY;
- $order = PaymentOrder::create(['type' => $type, 'member_id' => $user->member_id, 'amount' => $amount, 'channel' => self::CHANNEL,
- 'order_no' => PaymentOrderService::createOrderNo('sim' . $type . '_', $user->member_id), 'remark' => '模拟充值',
- 'status' => $type === PaymentOrderService::TYPE_SELF_PAY ? PaymentOrderService::STATUS_AUDIT : PaymentOrderService::STATUS_PROCESS]
- + PaymentOrderService::feeFields($amount, 0));
- if ($result === 'fail') {
- $order->status = PaymentOrderService::STATUS_FAIL;
- $order->remark = '模拟充值失败';
- $order->save();
- } elseif ($result === 'success') {
- if ($type === PaymentOrderService::TYPE_PAY) {
- if (!PaymentOrderService::simulatePaySuccess($order)) throw new Exception('模拟充值入账失败', HttpStatus::CUSTOM_ERROR);
- } else {
- self::approveCompanyDeposit((int) $order->id);
- }
- }
- return $order->refresh();
- }
- /** 与后台“人工充值审核通过”相同的入账:订单置成功、加可用余额、写“充值”流水。 */
- private static function approveCompanyDeposit(int $id): void
- {
- DB::transaction(function () use ($id) {
- $order = PaymentOrder::where('id', $id)->where('type', PaymentOrderService::TYPE_SELF_PAY)->lockForUpdate()->first();
- if (!$order || (int) $order->status !== PaymentOrderService::STATUS_AUDIT) throw new Exception('订单状态不可审核', HttpStatus::CUSTOM_ERROR);
- $order->status = PaymentOrderService::STATUS_SUCCESS;
- $order->state = 1;
- $order->save();
- $wallet = Wallet::where('member_id', $order->member_id)->lockForUpdate()->first();
- if (!$wallet) throw new Exception('钱包不存在', HttpStatus::CUSTOM_ERROR);
- $before = (string) $wallet->available_balance;
- $after = bcadd($before, (string) $order->amount, 10);
- $wallet->available_balance = $after;
- $wallet->save();
- $log = BalanceLogService::addLog($order->member_id, $order->amount, $before, $after, '充值', $order->id, '模拟充值');
- \App\Services\RechargeBonusService::grant('payment_order', $order, $log);
- });
- }
- /** 人工提现申请:校验打码量、冻结资金、建待审核提现单。 */
- public static function withdrawApply($memberId, $amount): PaymentOrder
- {
- SimulationSettings::assertEnabled();
- $user = SimulationSettings::member($memberId);
- $amount = SimulationSettings::assertAmount($amount);
- if (SimulationSettings::get('simulation_wager_bypass') === '1' && WageringService::enabled()) {
- $wallet = Wallet::where('member_id', $user->member_id)->first();
- if ($wallet) WageringService::resetCycle($wallet, 'simulation', null, '模拟提现跳过打码量');
- }
- $data = ['type' => PaymentOrderService::TYPE_SELF_PAYOUT, 'member_id' => $user->member_id, 'amount' => $amount, 'channel' => self::CHANNEL,
- 'order_no' => PaymentOrderService::createOrderNo('sim4_', $user->member_id), 'bank_name' => '模拟银行', 'account' => (string) $user->first_name,
- 'card_no' => 'SIM' . $user->member_id, 'status' => PaymentOrderService::STATUS_STAY] + PaymentOrderService::feeFields($amount, 0);
- [$order] = WithdrawFundsService::hold((string) $user->member_id, $amount, '人工提现', '模拟提现', WithdrawFundsService::PAYMENT_ORDERS,
- function () use ($data, $user) {
- $pending = PaymentOrder::where('type', PaymentOrderService::TYPE_SELF_PAYOUT)->where('member_id', $user->member_id)
- ->where('status', PaymentOrderService::STATUS_STAY)->lockForUpdate()->first(['id']);
- if ($pending) throw new Exception('该模拟会员已有一笔待审核的提现', HttpStatus::CUSTOM_ERROR);
- return PaymentOrder::create($data);
- });
- return $order->refresh();
- }
- /** 审核模拟提现:通过则扣除冻结余额,拒绝则退回可用余额。 */
- public static function withdrawAudit(int $id, bool $approve, string $remark = ''): PaymentOrder
- {
- SimulationSettings::assertEnabled();
- $order = PaymentOrder::where('id', $id)->where('type', PaymentOrderService::TYPE_SELF_PAYOUT)->first();
- if (!$order) throw new Exception('提现单不存在', HttpStatus::CUSTOM_ERROR);
- SimulationSettings::member($order->member_id);
- if ($approve) {
- DB::transaction(function () use ($id) {
- $locked = PaymentOrder::where('id', $id)->where('status', PaymentOrderService::STATUS_STAY)->lockForUpdate()->first();
- if (!$locked) throw new Exception('提现单已处理', HttpStatus::CUSTOM_ERROR);
- $locked->status = PaymentOrderService::STATUS_SUCCESS;
- $locked->save();
- WithdrawFundsService::settle(WithdrawFundsService::PAYMENT_ORDERS, $locked);
- });
- } else {
- $result = PaymentOrderService::withdrawalFailed($id, $remark !== '' ? $remark : '模拟提现拒绝');
- if ($result['code'] !== 0) throw new Exception($result['msg'], HttpStatus::CUSTOM_ERROR);
- }
- return $order->refresh();
- }
- /** 人工调账:加款记“人工充值”,扣款记“人工扣款”,与后台上下分一致。 */
- public static function adjust($memberId, $amount, string $direction, string $remark)
- {
- SimulationSettings::assertEnabled();
- $user = SimulationSettings::member($memberId);
- $amount = SimulationSettings::assertAmount($amount);
- return DB::transaction(function () use ($user, $amount, $direction, $remark) {
- $wallet = Wallet::where('member_id', $user->member_id)->lockForUpdate()->first();
- if (!$wallet) throw new Exception('钱包不存在', HttpStatus::CUSTOM_ERROR);
- $delta = $direction === 'debit' ? bcsub('0', $amount, 2) : $amount;
- $before = (string) $wallet->available_balance;
- $after = bcadd($before, $delta, 10);
- if (bccomp($after, '0', 10) < 0) throw new Exception('可用余额不足', HttpStatus::CUSTOM_ERROR);
- $wallet->available_balance = $after;
- $wallet->save();
- return BalanceLogService::addLog($user->member_id, $delta, $before, $after, $direction === 'debit' ? '人工扣款' : '人工充值', null, $remark);
- });
- }
- public static function freeze($memberId, $amount, string $reason, int $adminId, bool $unfreeze = false): array
- {
- SimulationSettings::assertEnabled();
- $user = SimulationSettings::member($memberId);
- $amount = SimulationSettings::assertAmount($amount);
- $requestId = 'sim-' . bin2hex(random_bytes(8));
- return $unfreeze
- ? WalletFreezeService::unfreeze($requestId, $adminId, (string) $user->member_id, $amount, '0', $reason)
- : WalletFreezeService::freeze($requestId, $adminId, (string) $user->member_id, $amount, '0', $reason);
- }
- }
|