донат шоп, управление магазином, яндекс метрика
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\BalanceTransaction;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class BalanceService
|
||||
{
|
||||
public function balance(int $accountId): string
|
||||
{
|
||||
$value = DB::connection('azerothcore_auth')->table('account_balances')
|
||||
->where('account_id', $accountId)
|
||||
->value('balance');
|
||||
|
||||
return $this->fromCents($this->toCents((string) ($value ?? '0')));
|
||||
}
|
||||
|
||||
/** @return Collection<int, BalanceTransaction> */
|
||||
public function recentTransactions(int $accountId, int $limit = 10): Collection
|
||||
{
|
||||
return BalanceTransaction::query()
|
||||
->where('account_id', $accountId)
|
||||
->latest('id')
|
||||
->limit($limit)
|
||||
->get();
|
||||
}
|
||||
|
||||
public function spend(int $accountId, string $amount, string $reference, ?string $description = null): BalanceTransaction
|
||||
{
|
||||
$cents = $this->toCents($amount);
|
||||
if ($cents <= 0) {
|
||||
throw new InvalidArgumentException('Сумма списания должна быть больше нуля.');
|
||||
}
|
||||
|
||||
return DB::connection('azerothcore_auth')->transaction(function () use ($accountId, $cents, $reference, $description): BalanceTransaction {
|
||||
$existing = BalanceTransaction::query()->where('reference', $reference)->first();
|
||||
if ($existing) {
|
||||
if ($existing->account_id !== $accountId || $this->toCents((string) $existing->amount) !== -$cents) {
|
||||
throw new InvalidArgumentException('Ключ операции уже использован с другими параметрами.');
|
||||
}
|
||||
|
||||
return $existing;
|
||||
}
|
||||
|
||||
$balance = DB::connection('azerothcore_auth')->table('account_balances')
|
||||
->where('account_id', $accountId)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
$current = $this->toCents((string) ($balance->balance ?? '0'));
|
||||
|
||||
if ($current < $cents) {
|
||||
throw new InvalidArgumentException('Недостаточно средств.');
|
||||
}
|
||||
|
||||
$after = $this->fromCents($current - $cents);
|
||||
DB::connection('azerothcore_auth')->table('account_balances')
|
||||
->where('account_id', $accountId)
|
||||
->update(['balance' => $after, 'updated_at' => now()]);
|
||||
|
||||
return BalanceTransaction::query()->create([
|
||||
'account_id' => $accountId,
|
||||
'type' => BalanceTransaction::TYPE_SPEND,
|
||||
'amount' => $this->fromCents(-$cents),
|
||||
'balance_after' => $after,
|
||||
'reference' => $reference,
|
||||
'metadata' => array_filter(['description' => $description]),
|
||||
]);
|
||||
}, 3);
|
||||
}
|
||||
|
||||
public function adjustByAdmin(
|
||||
int $accountId,
|
||||
string $amount,
|
||||
string $direction,
|
||||
int $adminAccountId,
|
||||
string $adminUsername,
|
||||
string $reason,
|
||||
): BalanceTransaction {
|
||||
$cents = $this->toCents($amount);
|
||||
if ($cents <= 0 || ! in_array($direction, ['credit', 'debit'], true)) {
|
||||
throw new InvalidArgumentException('Некорректная корректировка баланса.');
|
||||
}
|
||||
|
||||
return DB::connection('azerothcore_auth')->transaction(function () use ($accountId, $cents, $direction, $adminAccountId, $adminUsername, $reason): BalanceTransaction {
|
||||
DB::connection('azerothcore_auth')->table('account_balances')->insertOrIgnore([
|
||||
'account_id' => $accountId,
|
||||
'balance' => '0.00',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
$balance = DB::connection('azerothcore_auth')->table('account_balances')
|
||||
->where('account_id', $accountId)
|
||||
->lockForUpdate()
|
||||
->firstOrFail();
|
||||
$current = $this->toCents((string) $balance->balance);
|
||||
$delta = $direction === 'credit' ? $cents : -$cents;
|
||||
|
||||
if ($current + $delta < 0) {
|
||||
throw new InvalidArgumentException('Недостаточно средств для ручного списания.');
|
||||
}
|
||||
|
||||
$after = $this->fromCents($current + $delta);
|
||||
DB::connection('azerothcore_auth')->table('account_balances')
|
||||
->where('account_id', $accountId)
|
||||
->update(['balance' => $after, 'updated_at' => now()]);
|
||||
|
||||
return BalanceTransaction::query()->create([
|
||||
'account_id' => $accountId,
|
||||
'type' => $direction === 'credit' ? BalanceTransaction::TYPE_ADMIN_CREDIT : BalanceTransaction::TYPE_ADMIN_DEBIT,
|
||||
'amount' => $this->fromCents($delta),
|
||||
'balance_after' => $after,
|
||||
'reference' => 'admin:'.Str::uuid(),
|
||||
'metadata' => [
|
||||
'admin_account_id' => $adminAccountId,
|
||||
'admin_username' => $adminUsername,
|
||||
'reason' => $reason,
|
||||
],
|
||||
]);
|
||||
}, 3);
|
||||
}
|
||||
|
||||
/** @return Collection<int, BalanceTransaction> */
|
||||
public function adminTransactions(?string $search = null, int $limit = 100): Collection
|
||||
{
|
||||
return BalanceTransaction::query()
|
||||
->leftJoin('account', 'account.id', '=', 'balance_transactions.account_id')
|
||||
->select('balance_transactions.*', 'account.username')
|
||||
->when($search, function ($query, string $search): void {
|
||||
$query->where(function ($query) use ($search): void {
|
||||
$query->where('account.username', 'like', '%'.$search.'%');
|
||||
if (ctype_digit($search)) {
|
||||
$query->orWhere('balance_transactions.account_id', (int) $search);
|
||||
}
|
||||
});
|
||||
})
|
||||
->latest('balance_transactions.id')
|
||||
->limit($limit)
|
||||
->get();
|
||||
}
|
||||
|
||||
private function toCents(string $amount): int
|
||||
{
|
||||
$normalized = str_replace(',', '.', trim($amount));
|
||||
if (! preg_match('/^-?\d+(?:\.\d{1,6})?$/', $normalized)) {
|
||||
throw new InvalidArgumentException('Некорректная сумма.');
|
||||
}
|
||||
|
||||
$negative = str_starts_with($normalized, '-');
|
||||
$normalized = ltrim($normalized, '-');
|
||||
[$whole, $fraction] = array_pad(explode('.', $normalized, 2), 2, '');
|
||||
$cents = ((int) $whole * 100) + (int) str_pad(substr($fraction, 0, 2), 2, '0');
|
||||
|
||||
return $negative ? -$cents : $cents;
|
||||
}
|
||||
|
||||
private function fromCents(int $cents): string
|
||||
{
|
||||
return number_format($cents / 100, 2, '.', '');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user