From 9736dc65d8569172f1b06d960bb294fa70d1920e Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Mon, 1 Nov 2021 21:20:39 +0300 Subject: [PATCH 01/51] [7.x] drop @deprecated classes --- .phpstorm.meta.php | 3 -- config/config.php | 16 ------- src/Interfaces/Mathable.php | 15 ------- src/Interfaces/Rateable.php | 26 ----------- src/Interfaces/Storable.php | 46 -------------------- src/Services/CommonService.php | 15 ++++--- src/Services/ExchangeService.php | 35 --------------- src/Services/WalletService.php | 17 ++++---- src/Simple/Rate.php | 60 ------------------------- src/Simple/Store.php | 53 ---------------------- src/Traits/CanExchange.php | 11 +++-- src/Traits/HasWallet.php | 6 ++- src/WalletServiceProvider.php | 13 ------ tests/BalanceTest.php | 11 ++--- tests/Common/MyExchange.php | 44 +++++++++++++++++++ tests/Common/Rate.php | 72 ------------------------------ tests/ExchangeTest.php | 56 ++++++++++++++++++++++++ tests/RateTest.php | 75 -------------------------------- tests/SingletonTest.php | 24 ---------- tests/TestCase.php | 18 ++------ 20 files changed, 138 insertions(+), 478 deletions(-) delete mode 100644 src/Interfaces/Mathable.php delete mode 100644 src/Interfaces/Rateable.php delete mode 100644 src/Interfaces/Storable.php delete mode 100644 src/Services/ExchangeService.php delete mode 100644 src/Simple/Rate.php delete mode 100644 src/Simple/Store.php create mode 100644 tests/Common/MyExchange.php delete mode 100644 tests/Common/Rate.php delete mode 100644 tests/RateTest.php diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index 0207165d3..ea89c12b3 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -27,9 +27,6 @@ Wallet::class => Wallet::class, Transfer::class => Transfer::class, Transaction::class => Transaction::class, - Mathable::class => Mathable::class, - Rateable::class => Rateable::class, - Storable::class => Storable::class, ])); } diff --git a/config/config.php b/config/config.php index 1bbc084f5..8f438a2b6 100644 --- a/config/config.php +++ b/config/config.php @@ -31,8 +31,6 @@ */ 'package' => [ 'exchange' => Exchange::class, - 'rateable' => Rate::class, - 'storable' => Store::class, 'mathable' => MathService::class, ], @@ -52,20 +50,6 @@ 'seconds' => 1, ], - /** - * Sometimes a slug may not match the currency and you need the ability to add an exception. - * The main thing is that there are not many exceptions). - * - * Syntax: - * 'slug' => 'currency' - * - * @example - * 'my-usd' => 'USD' - * - * @deprecated use table "wallets", column meta.currency - */ - 'currencies' => [], - /** * Services are the main core of the library and sometimes they need to be improved. * This configuration will help you to quickly customize the library. diff --git a/src/Interfaces/Mathable.php b/src/Interfaces/Mathable.php deleted file mode 100644 index 634ac0abe..000000000 --- a/src/Interfaces/Mathable.php +++ /dev/null @@ -1,15 +0,0 @@ -dbService = $dbService; $this->lockService = $lockService; $this->math = $math; $this->walletService = $walletService; - $this->store = $store; + $this->bookkeeper = $bookkeeper; $this->consistency = $consistency; } @@ -238,7 +238,8 @@ public function addBalance(Wallet $wallet, $amount): bool { return $this->lockService->lock($this, __FUNCTION__, function () use ($wallet, $amount) { /** @var WalletModel $wallet */ - $balance = $this->store->incBalance($wallet, $amount); + $walletObject = $this->walletService->getWallet($wallet); + $balance = $this->bookkeeper->increase($walletObject, $amount); try { $result = $wallet->newQuery() @@ -246,7 +247,7 @@ public function addBalance(Wallet $wallet, $amount): bool ->update(compact('balance')) ; } catch (Throwable $throwable) { - $this->store->setBalance($wallet, $wallet->getAvailableBalance()); + $this->bookkeeper->sync($walletObject, $wallet->getAvailableBalance()); throw $throwable; } @@ -256,7 +257,7 @@ public function addBalance(Wallet $wallet, $amount): bool ->syncOriginalAttributes('balance') ; } else { - $this->store->setBalance($wallet, $wallet->getAvailableBalance()); + $this->bookkeeper->sync($walletObject, $wallet->getAvailableBalance()); } return $result; diff --git a/src/Services/ExchangeService.php b/src/Services/ExchangeService.php deleted file mode 100644 index b1ac142fe..000000000 --- a/src/Services/ExchangeService.php +++ /dev/null @@ -1,35 +0,0 @@ -rate = $rate; - } - - /** - * @return float|int - */ - public function rate(Wallet $from, Wallet $to) - { - return $this->rate - ->withAmount(1) - ->withCurrency($from) - ->convertTo($to) - ; - } -} diff --git a/src/Services/WalletService.php b/src/Services/WalletService.php index 65507c98a..b13fc5f5d 100644 --- a/src/Services/WalletService.php +++ b/src/Services/WalletService.php @@ -6,9 +6,9 @@ use Bavix\Wallet\Interfaces\Customer; use Bavix\Wallet\Interfaces\Discount; use Bavix\Wallet\Interfaces\MinimalTaxable; -use Bavix\Wallet\Interfaces\Storable; use Bavix\Wallet\Interfaces\Taxable; use Bavix\Wallet\Interfaces\Wallet; +use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Models\Wallet as WalletModel; @@ -21,19 +21,19 @@ class WalletService private DbService $dbService; private MathInterface $math; private LockService $lockService; - private Storable $store; + private BookkeeperInterface $bookkeeper; public function __construct( DbService $dbService, MathInterface $math, LockService $lockService, - Storable $store, + BookkeeperInterface $bookkeeper, ConsistencyInterface $consistency ) { $this->dbService = $dbService; $this->math = $math; $this->lockService = $lockService; - $this->store = $store; + $this->bookkeeper = $bookkeeper; $this->consistency = $consistency; } @@ -148,12 +148,11 @@ public function getWallet(Wallet $object, bool $autoSave = true): WalletModel public function refresh(WalletModel $wallet): bool { return $this->lockService->lock($this, __FUNCTION__, function () use ($wallet) { - $this->store->getBalance($wallet); $whatIs = $wallet->balance; $balance = $wallet->getAvailableBalance(); - $wallet->balance = $balance; + $wallet->balance = (string) $balance; - return $this->store->setBalance($wallet, $balance) && + return $this->bookkeeper->sync($this->getWallet($wallet), $balance) && (!$this->math->compare($whatIs, $balance) || $wallet->save()); }); } @@ -167,8 +166,8 @@ public function refresh(WalletModel $wallet): bool public function adjustment(WalletModel $wallet, ?array $meta = null): void { $this->dbService->transaction(function () use ($wallet, $meta) { - $this->store->getBalance($wallet); - $adjustmentBalance = $wallet->balance; + $walletObject = $this->getWallet($wallet); + $adjustmentBalance = $this->bookkeeper->amount($walletObject); $wallet->refreshBalance(); $difference = $this->math->sub($wallet->balance, $adjustmentBalance); diff --git a/src/Simple/Rate.php b/src/Simple/Rate.php deleted file mode 100644 index 9b7e5f125..000000000 --- a/src/Simple/Rate.php +++ /dev/null @@ -1,60 +0,0 @@ -exchange = $exchange; - } - - /** - * {@inheritdoc} - */ - public function withAmount($amount): Rateable - { - $this->amount = $amount; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function withCurrency(Wallet $wallet): Rateable - { - $this->withCurrency = $wallet; - - return $this; - } - - /** - * {@inheritdoc} - */ - public function convertTo(Wallet $wallet) - { - /** @var \Bavix\Wallet\Models\Wallet $wallet */ - return $this->exchange->convertTo( - $this->withCurrency->currency, - $wallet->currency, - $this->amount - ); - } -} diff --git a/src/Simple/Store.php b/src/Simple/Store.php deleted file mode 100644 index 355b5d680..000000000 --- a/src/Simple/Store.php +++ /dev/null @@ -1,53 +0,0 @@ -getWallet($object); - - return app(BookkeeperInterface::class)->amount($wallet); - } - - /** - * {@inheritdoc} - */ - public function incBalance($object, $amount): string - { - $wallet = app(WalletService::class)->getWallet($object); - - return app(BookkeeperInterface::class)->increase($wallet, $amount); - } - - /** - * {@inheritdoc} - */ - public function setBalance($object, $amount): bool - { - $wallet = app(WalletService::class)->getWallet($object); - - return app(BookkeeperInterface::class)->sync($wallet, $amount); - } - - /** - * {@inheritdoc} - */ - public function fresh(): bool - { - return app(StorageInterface::class)->flush(); - } -} diff --git a/src/Traits/CanExchange.php b/src/Traits/CanExchange.php index a820687ce..e58552f50 100644 --- a/src/Traits/CanExchange.php +++ b/src/Traits/CanExchange.php @@ -4,12 +4,12 @@ use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Internal\ConsistencyInterface; +use Bavix\Wallet\Internal\ExchangeInterface; use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Services\CommonService; use Bavix\Wallet\Services\DbService; -use Bavix\Wallet\Services\ExchangeService; use Bavix\Wallet\Services\LockService; use Bavix\Wallet\Services\WalletService; @@ -49,9 +49,14 @@ public function forceExchange(Wallet $to, $amount, ?array $meta = null): Transfe return app(LockService::class)->lock($this, __FUNCTION__, static function () use ($from, $to, $amount, $meta) { return app(DbService::class)->transaction(static function () use ($from, $to, $amount, $meta) { + $walletService = app(WalletService::class); $math = app(MathInterface::class); - $rate = app(ExchangeService::class)->rate($from, $to); - $fee = app(WalletService::class)->fee($to, $amount); + $fee = $walletService->fee($to, $amount); + $rate = app(ExchangeInterface::class)->convertTo( + $walletService->getWallet($from)->currency, + $walletService->getWallet($to)->currency, + 1 + ); $withdraw = app(CommonService::class) ->forceWithdraw($from, $math->add($amount, $fee), $meta) diff --git a/src/Traits/HasWallet.php b/src/Traits/HasWallet.php index 537efd219..9175c54f4 100644 --- a/src/Traits/HasWallet.php +++ b/src/Traits/HasWallet.php @@ -6,8 +6,8 @@ use Bavix\Wallet\Exceptions\AmountInvalid; use Bavix\Wallet\Exceptions\BalanceIsEmpty; use Bavix\Wallet\Exceptions\InsufficientFunds; -use Bavix\Wallet\Interfaces\Storable; use Bavix\Wallet\Interfaces\Wallet; +use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Models\Transaction; @@ -80,7 +80,9 @@ public function deposit($amount, ?array $meta = null, bool $confirmed = true): T public function getBalanceAttribute() { /** @var Wallet $this */ - return app(Storable::class)->getBalance($this); + return app(BookkeeperInterface::class)->amount( + app(WalletService::class)->getWallet($this) + ); } /** diff --git a/src/WalletServiceProvider.php b/src/WalletServiceProvider.php index 8f353d956..330322689 100644 --- a/src/WalletServiceProvider.php +++ b/src/WalletServiceProvider.php @@ -5,9 +5,6 @@ namespace Bavix\Wallet; use Bavix\Wallet\Commands\RefreshBalance; -use Bavix\Wallet\Interfaces\Mathable; -use Bavix\Wallet\Interfaces\Rateable; -use Bavix\Wallet\Interfaces\Storable; use Bavix\Wallet\Internal\BasketInterface; use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; @@ -30,7 +27,6 @@ use Bavix\Wallet\Services\CommonService; use Bavix\Wallet\Services\ConsistencyService; use Bavix\Wallet\Services\DbService; -use Bavix\Wallet\Services\ExchangeService; use Bavix\Wallet\Services\LockService; use Bavix\Wallet\Services\MathService; use Bavix\Wallet\Services\MetaService; @@ -38,10 +34,7 @@ use Bavix\Wallet\Services\StorageService; use Bavix\Wallet\Services\UuidFactoryService; use Bavix\Wallet\Services\WalletService; -use Bavix\Wallet\Simple\BrickMath; use Bavix\Wallet\Simple\Exchange; -use Bavix\Wallet\Simple\Rate; -use Bavix\Wallet\Simple\Store; use function config; use function dirname; use function function_exists; @@ -124,12 +117,6 @@ private function singletons(): void private function legacySingleton(): void { - $this->app->singleton(ExchangeService::class, config('wallet.services.exchange', ExchangeService::class)); - $this->app->singleton(Rateable::class, config('wallet.package.rateable', Rate::class)); - $this->app->singleton(Storable::class, config('wallet.package.storable', Store::class)); - - $this->app->singleton(Mathable::class, BrickMath::class); - $this->app->singleton(DbService::class, config('wallet.services.db', DbService::class)); $this->app->singleton(LockService::class, config('wallet.services.lock', LockService::class)); $this->app->singleton(MetaService::class); diff --git a/tests/BalanceTest.php b/tests/BalanceTest.php index 7c02b1494..07b98a587 100644 --- a/tests/BalanceTest.php +++ b/tests/BalanceTest.php @@ -3,7 +3,8 @@ namespace Bavix\Wallet\Test; use function app; -use Bavix\Wallet\Interfaces\Storable; +use Bavix\Wallet\Internal\BookkeeperInterface; +use Bavix\Wallet\Internal\StorageInterface; use Bavix\Wallet\Models\Wallet; use Bavix\Wallet\Services\CommonService; use Bavix\Wallet\Services\WalletService; @@ -134,7 +135,7 @@ public function testGetBalance(): void self::assertEquals($wallet->balance, 0); self::assertTrue($wallet->exists); - self::assertEquals(0, app(Storable::class)->getBalance($wallet)); + self::assertEquals(0, app(BookkeeperInterface::class)->amount($wallet)); } /** @@ -166,7 +167,7 @@ public function testFailUpdate(): void ; self::assertFalse($result); - self::assertEquals(0, app(Storable::class)->getBalance($wallet)); + self::assertEquals(0, app(BookkeeperInterface::class)->amount($wallet)); } /** @@ -292,7 +293,7 @@ public function testForceUpdate(): void * * Here is an example: */ - app(Storable::class)->fresh(); + app(StorageInterface::class)->flush(); self::assertEquals(1000, $wallet->getRawOriginal('balance')); /** @@ -336,7 +337,7 @@ public function testAdjustment(int $account, int $adjust): void * * Here is an example: */ - app(Storable::class)->fresh(); + app(StorageInterface::class)->flush(); self::assertEquals($account, $wallet->getRawOriginal('balance')); /** diff --git a/tests/Common/MyExchange.php b/tests/Common/MyExchange.php new file mode 100644 index 000000000..22d08edb8 --- /dev/null +++ b/tests/Common/MyExchange.php @@ -0,0 +1,44 @@ + [ + 'RUB' => 67.61, + ], + ]; + + private MathInterface $math; + + /** + * Rate constructor. + */ + public function __construct(MathInterface $mathService) + { + $this->math = $mathService; + + foreach ($this->rates as $from => $rates) { + foreach ($rates as $to => $rate) { + if (empty($this->rates[$to][$from])) { + $this->rates[$to][$from] = $this->math->div(1, $rate); + } + } + } + } + + /** @param float|int|string $amount */ + public function convertTo(string $fromCurrency, string $toCurrency, $amount): string + { + return $this->math->mul($amount, Arr::get( + Arr::get($this->rates, $fromCurrency, []), + $toCurrency, + 1 + )); + } +} diff --git a/tests/Common/Rate.php b/tests/Common/Rate.php deleted file mode 100644 index 752547abf..000000000 --- a/tests/Common/Rate.php +++ /dev/null @@ -1,72 +0,0 @@ - [ - 'RUB' => 67.61, - ], - ]; - - private $walletService; - - private $mathService; - - /** - * Rate constructor. - */ - public function __construct(ExchangeInterface $exchange, MathInterface $mathService, WalletService $walletService) - { - parent::__construct($exchange); - $this->walletService = $walletService; - $this->mathService = $mathService; - - foreach ($this->rates as $from => $rates) { - foreach ($rates as $to => $rate) { - if (empty($this->rates[$to][$from])) { - $this->rates[$to][$from] = $this->mathService->div(1, $rate); - } - } - } - } - - /** - * {@inheritdoc} - */ - public function convertTo(Wallet $wallet) - { - return $this->mathService->mul( - parent::convertTo($wallet), - $this->rate($wallet) - ); - } - - /** - * @return float|int - */ - protected function rate(Wallet $wallet) - { - $from = $this->walletService->getWallet($this->withCurrency); - $to = $this->walletService->getWallet($wallet); - - /** - * @var \Bavix\Wallet\Models\Wallet $wallet - */ - return Arr::get( - Arr::get($this->rates, $from->currency, []), - $to->currency, - 1 - ); - } -} diff --git a/tests/ExchangeTest.php b/tests/ExchangeTest.php index 47b122805..ad4ab17b0 100644 --- a/tests/ExchangeTest.php +++ b/tests/ExchangeTest.php @@ -2,9 +2,12 @@ namespace Bavix\Wallet\Test; +use Bavix\Wallet\Internal\ExchangeInterface; use Bavix\Wallet\Models\Transfer; +use Bavix\Wallet\Simple\Exchange; use Bavix\Wallet\Test\Factories\UserMultiFactory; use Bavix\Wallet\Test\Models\UserMulti; +use Illuminate\Support\Str; /** * @internal @@ -66,4 +69,57 @@ public function testSafe(): void $transfer = $rub->safeExchange($usd, 10000); self::assertNull($transfer); } + + public function testExchangeClass(): void + { + $service = app(Exchange::class); + + self::assertEquals(1, $service->convertTo('USD', 'EUR', 1)); + self::assertEquals(5, $service->convertTo('USD', 'EUR', 5)); + self::assertEquals(27, $service->convertTo('USD', 'EUR', 27)); + } + + public function testRate(): void + { + /** @var UserMulti $user */ + $user = UserMultiFactory::new()->create(); + $usd = $user->createWallet(['name' => 'Dollar USA', 'slug' => 'my-usd', 'meta' => ['currency' => 'USD']]); + self::assertEquals($usd->slug, 'my-usd'); + self::assertEquals($usd->currency, 'USD'); + self::assertEquals($usd->holder_id, $user->id); + self::assertInstanceOf($usd->holder_type, $user); + + $rub = $user->createWallet(['name' => 'RUB']); + self::assertEquals($rub->slug, 'rub'); + self::assertEquals($rub->currency, 'RUB'); + self::assertEquals($rub->holder_id, $user->id); + self::assertInstanceOf($rub->holder_type, $user); + + $superWallet = $user->createWallet(['name' => 'Super Wallet']); + self::assertEquals($superWallet->slug, Str::slug('Super Wallet')); + self::assertEquals($superWallet->currency, Str::upper(Str::slug('Super Wallet'))); + self::assertEquals($superWallet->holder_id, $user->id); + self::assertInstanceOf($superWallet->holder_type, $user); + + $rate = app(ExchangeInterface::class) + ->convertTo($usd->currency, $rub->currency, 1000) + ; + + self::assertEquals(67610., $rate); + } + + public function testExchange(): void + { + $rate = app(ExchangeInterface::class) + ->convertTo('USD', 'RUB', 1) + ; + + self::assertEquals(67.61, $rate); + + $rate = app(ExchangeInterface::class) + ->convertTo('RUB', 'USD', 1) + ; + + self::assertEquals(1 / 67.61, $rate); + } } diff --git a/tests/RateTest.php b/tests/RateTest.php deleted file mode 100644 index 1ec30916e..000000000 --- a/tests/RateTest.php +++ /dev/null @@ -1,75 +0,0 @@ -create(); - $usd = $user->createWallet(['name' => 'Dollar USA', 'slug' => 'my-usd']); - self::assertEquals($usd->slug, 'my-usd'); - self::assertEquals($usd->currency, 'USD'); - self::assertEquals($usd->holder_id, $user->id); - self::assertInstanceOf($usd->holder_type, $user); - - $rub = $user->createWallet(['name' => 'RUB']); - self::assertEquals($rub->slug, 'rub'); - self::assertEquals($rub->currency, 'RUB'); - self::assertEquals($rub->holder_id, $user->id); - self::assertInstanceOf($rub->holder_type, $user); - - $superWallet = $user->createWallet(['name' => 'Super Wallet']); - self::assertEquals($superWallet->slug, Str::slug('Super Wallet')); - self::assertEquals($superWallet->currency, Str::upper(Str::slug('Super Wallet'))); - self::assertEquals($superWallet->holder_id, $user->id); - self::assertInstanceOf($superWallet->holder_type, $user); - - $rate = app(Rateable::class) - ->withAmount(1000) - ->withCurrency($usd) - ->convertTo($rub) - ; - - self::assertEquals($rate, 67610.); - } - - public function testExchange(): void - { - /** @var UserMulti $user */ - $user = UserMultiFactory::new()->create(); - $usd = $user->createWallet(['name' => 'USD']); - self::assertEquals($usd->slug, 'usd'); - self::assertEquals($usd->currency, 'USD'); - self::assertEquals($usd->holder_id, $user->id); - self::assertInstanceOf($usd->holder_type, $user); - - $rub = $user->createWallet(['name' => 'RUR', 'slug' => 'my-rub']); - self::assertEquals($rub->slug, 'my-rub'); - self::assertEquals($rub->currency, 'RUB'); - self::assertEquals($rub->holder_id, $user->id); - self::assertInstanceOf($rub->holder_type, $user); - - $rate = app(ExchangeService::class) - ->rate($usd, $rub) - ; - - self::assertEquals($rate, 67.61); - - $rate = app(ExchangeService::class) - ->rate($rub, $usd) - ; - - self::assertEquals($rate, 1 / 67.61); - } -} diff --git a/tests/SingletonTest.php b/tests/SingletonTest.php index 08c9dc53f..4b65b83be 100644 --- a/tests/SingletonTest.php +++ b/tests/SingletonTest.php @@ -2,9 +2,6 @@ namespace Bavix\Wallet\Test; -use Bavix\Wallet\Interfaces\Mathable; -use Bavix\Wallet\Interfaces\Rateable; -use Bavix\Wallet\Interfaces\Storable; use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Objects\Cart; @@ -12,7 +9,6 @@ use Bavix\Wallet\Objects\Operation; use Bavix\Wallet\Services\CommonService; use Bavix\Wallet\Services\DbService; -use Bavix\Wallet\Services\ExchangeService; use Bavix\Wallet\Services\LockService; use Bavix\Wallet\Services\WalletService; use Bavix\Wallet\Test\Common\Models\Transaction; @@ -44,21 +40,6 @@ public function testOperation(): void self::assertNotEquals($this->getRefId(Operation::class), $this->getRefId(Operation::class)); } - public function testRateable(): void - { - self::assertEquals($this->getRefId(Rateable::class), $this->getRefId(Rateable::class)); - } - - public function testStorable(): void - { - self::assertEquals($this->getRefId(Storable::class), $this->getRefId(Storable::class)); - } - - public function testMathable(): void - { - self::assertEquals($this->getRefId(Mathable::class), $this->getRefId(Mathable::class)); - } - public function testMathInterface(): void { self::assertEquals($this->getRefId(MathInterface::class), $this->getRefId(MathInterface::class)); @@ -79,11 +60,6 @@ public function testWallet(): void self::assertNotEquals($this->getRefId(Wallet::class), $this->getRefId(Wallet::class)); } - public function testExchangeService(): void - { - self::assertEquals($this->getRefId(ExchangeService::class), $this->getRefId(ExchangeService::class)); - } - public function testCommonService(): void { self::assertEquals($this->getRefId(CommonService::class), $this->getRefId(CommonService::class)); diff --git a/tests/TestCase.php b/tests/TestCase.php index 5c1567b47..8922d231d 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,13 +2,12 @@ namespace Bavix\Wallet\Test; -use Bavix\Wallet\Interfaces\Storable; +use Bavix\Wallet\Internal\StorageInterface; use Bavix\Wallet\Services\MathService; -use Bavix\Wallet\Simple\Store; use Bavix\Wallet\Test\Common\Models\Transaction; use Bavix\Wallet\Test\Common\Models\Transfer; use Bavix\Wallet\Test\Common\Models\Wallet; -use Bavix\Wallet\Test\Common\Rate; +use Bavix\Wallet\Test\Common\MyExchange; use Bavix\Wallet\Test\Common\WalletServiceProvider; use Illuminate\Config\Repository; use Illuminate\Foundation\Application; @@ -25,7 +24,7 @@ class TestCase extends OrchestraTestCase public function setUp(): void { parent::setUp(); - app(Storable::class)->fresh(); + app(StorageInterface::class)->flush(); } public function expectExceptionMessageStrict(string $message): void @@ -49,8 +48,7 @@ protected function updateConfig(Application $app): void $config = $app['config']; // Bind eloquent models to IoC container - $app['config']->set('wallet.package.rateable', Rate::class); - $app['config']->set('wallet.package.storable', Store::class); + $app['config']->set('wallet.package.exchange', MyExchange::class); $app['config']->set('wallet.package.mathable', MathService::class); // database @@ -75,14 +73,6 @@ protected function updateConfig(Application $app): void $config->set('wallet.transfer.model', Transfer::class); $config->set('wallet.wallet.model', Wallet::class); - // wallet - $config->set('wallet.currencies', [ - 'my-usd' => 'USD', - 'my-eur' => 'EUR', - 'my-rub' => 'RUB', - 'def-curr' => 'EUR', - ]); - $config->set('wallet.lock.enabled', false); } } From 407d468a85312e860fa8e2498fac873ef90ee8af Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Mon, 1 Nov 2021 21:33:06 +0300 Subject: [PATCH 02/51] drop deprecated methods & Math,Lock-classes --- src/Objects/Cart.php | 27 ------------ src/Objects/EmptyLock.php | 78 ---------------------------------- src/Services/CommonService.php | 16 ------- src/Services/WalletService.php | 40 ----------------- src/Simple/BrickMath.php | 16 ------- tests/CartTest.php | 5 ++- tests/EmptyLockTest.php | 30 ------------- tests/SingletonTest.php | 6 --- 8 files changed, 3 insertions(+), 215 deletions(-) delete mode 100644 src/Objects/EmptyLock.php delete mode 100644 src/Simple/BrickMath.php delete mode 100644 tests/EmptyLockTest.php diff --git a/src/Objects/Cart.php b/src/Objects/Cart.php index 178672fa9..2095e9c28 100644 --- a/src/Objects/Cart.php +++ b/src/Objects/Cart.php @@ -9,12 +9,9 @@ use Bavix\Wallet\Interfaces\Product; use Bavix\Wallet\Internal\BasketInterface; use Bavix\Wallet\Internal\CartInterface; -use Bavix\Wallet\Internal\Dto\AvailabilityDto; use Bavix\Wallet\Internal\Dto\BasketDto; use Bavix\Wallet\Internal\Dto\ItemDto; use Bavix\Wallet\Internal\MathInterface; -use Bavix\Wallet\Internal\PurchaseInterface; -use Bavix\Wallet\Models\Transfer; use function count; use Countable; use function get_class; @@ -97,30 +94,6 @@ public function getUniqueItems(): array return array_unique($this->items); } - /** - * The method returns the transfers already paid for the goods. - * - * @return Transfer[] - * - * @deprecated - * @see PurchaseInterface::already() - */ - public function alreadyBuy(Customer $customer, bool $gifts = false): array - { - return app(PurchaseInterface::class)->already($customer, $this->getBasketDto(), $gifts); - } - - /** - * @deprecated - * @see BasketInterface::availability() - * - * @codeCoverageIgnore - */ - public function canBuy(Customer $customer, bool $force = false): bool - { - return $this->basket->availability(new AvailabilityDto($customer, $this->getBasketDto(), $force)); - } - public function getTotal(Customer $customer): string { $result = 0; diff --git a/src/Objects/EmptyLock.php b/src/Objects/EmptyLock.php deleted file mode 100644 index f07aa893a..000000000 --- a/src/Objects/EmptyLock.php +++ /dev/null @@ -1,78 +0,0 @@ -ownerId) { - $this->ownerId = Str::random(); - } - - return $this->ownerId; - } - - /** - * Releases this lock in disregard of ownership. - * - * @codeCoverageIgnore - */ - public function forceRelease(): void - { - // force lock release - } -} diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 2d28c186c..8ad16c3d0 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -145,22 +145,6 @@ public function deposit(Wallet $wallet, $amount, ?array $meta, bool $confirmed = }); } - /** - * @param int|string $amount - * - * @throws BalanceIsEmpty - * @throws InsufficientFunds - * - * @deprecated - * @see ConsistencyInterface::potential() - * - * @codeCoverageIgnore - */ - public function verifyWithdraw(Wallet $wallet, $amount, bool $allowZero = false): void - { - $this->consistency->checkPotential($wallet, $amount, $allowZero); - } - /** * Create Operation without DB::transaction. * diff --git a/src/Services/WalletService.php b/src/Services/WalletService.php index b13fc5f5d..c855d121f 100644 --- a/src/Services/WalletService.php +++ b/src/Services/WalletService.php @@ -2,7 +2,6 @@ namespace Bavix\Wallet\Services; -use Bavix\Wallet\Exceptions\AmountInvalid; use Bavix\Wallet\Interfaces\Customer; use Bavix\Wallet\Interfaces\Discount; use Bavix\Wallet\Interfaces\MinimalTaxable; @@ -50,28 +49,6 @@ public function discount(Wallet $customer, Wallet $product): int return 0; } - /** - * @deprecated - * @see Wallet::$decimal_places - * - * @codeCoverageIgnore - */ - public function decimalPlacesValue(Wallet $object): int - { - return $this->getWallet($object)->decimal_places ?: 2; - } - - /** - * @deprecated - * @see MathInterface::powTen() - * - * @codeCoverageIgnore - */ - public function decimalPlaces(Wallet $object): string - { - return $this->math->powTen($this->getWallet($object)->decimal_places); - } - /** * Consider the fee that the system will receive. * @@ -107,23 +84,6 @@ public function fee(Wallet $wallet, $amount) return $fee; } - /** - * The amount of checks for errors. - * - * @param int|string $amount - * - * @throws AmountInvalid - * - * @deprecated - * @see ConsistencyInterface::checkPositive() - * - * @codeCoverageIgnore - */ - public function checkAmount($amount): void - { - $this->consistency->checkPositive($amount); - } - public function getWallet(Wallet $object, bool $autoSave = true): WalletModel { /** @var WalletModel $wallet */ diff --git a/src/Simple/BrickMath.php b/src/Simple/BrickMath.php deleted file mode 100644 index 01af29cf9..000000000 --- a/src/Simple/BrickMath.php +++ /dev/null @@ -1,16 +0,0 @@ -payCart($cart); self::assertCount(count($cart), $transfers); - self::assertTrue((bool) $cart->alreadyBuy($buyer)); + self::assertTrue((bool) app(PurchaseInterface::class)->already($buyer, $cart->getBasketDto())); self::assertEquals(0, $buyer->balance); foreach ($transfers as $transfer) { @@ -270,7 +271,7 @@ public function testWithdrawal(): void $transfers = $buyer->payCart($cart); self::assertCount(count($cart), $transfers); - self::assertTrue((bool) $cart->alreadyBuy($buyer)); + self::assertTrue((bool) app(PurchaseInterface::class)->already($buyer, $cart->getBasketDto())); self::assertEquals(0, $buyer->balance); foreach ($transfers as $transfer) { diff --git a/tests/EmptyLockTest.php b/tests/EmptyLockTest.php deleted file mode 100644 index 8bfdf3157..000000000 --- a/tests/EmptyLockTest.php +++ /dev/null @@ -1,30 +0,0 @@ -block(1)); - self::assertTrue($empty->block(1, null)); - self::assertNull($empty->get()); - self::assertTrue($empty->get(static function () { - return true; - })); - } - - public function testOwner(): void - { - $empty = app(EmptyLock::class); - $str = $empty->owner(); - self::assertIsString($str); - self::assertEquals($str, $empty->owner()); - } -} diff --git a/tests/SingletonTest.php b/tests/SingletonTest.php index 4b65b83be..b696a845a 100644 --- a/tests/SingletonTest.php +++ b/tests/SingletonTest.php @@ -5,7 +5,6 @@ use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Objects\Cart; -use Bavix\Wallet\Objects\EmptyLock; use Bavix\Wallet\Objects\Operation; use Bavix\Wallet\Services\CommonService; use Bavix\Wallet\Services\DbService; @@ -30,11 +29,6 @@ public function testCart(): void self::assertNotEquals($this->getRefId(Cart::class), $this->getRefId(Cart::class)); } - public function testEmptyLock(): void - { - self::assertNotEquals($this->getRefId(EmptyLock::class), $this->getRefId(EmptyLock::class)); - } - public function testOperation(): void { self::assertNotEquals($this->getRefId(Operation::class), $this->getRefId(Operation::class)); From e2a7bbb41a5bee895852941f7206b0eccdbf6c0e Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Mon, 1 Nov 2021 21:35:43 +0300 Subject: [PATCH 03/51] drop old logic --- src/Models/Wallet.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Models/Wallet.php b/src/Models/Wallet.php index c91b40c87..00047186b 100644 --- a/src/Models/Wallet.php +++ b/src/Models/Wallet.php @@ -156,10 +156,6 @@ public function holder(): MorphTo public function getCurrencyAttribute(): string { - $currencies = config('wallet.currencies', []); - - return $currencies[$this->slug] ?? - $this->meta['currency'] ?? - Str::upper($this->slug); + return $this->meta['currency'] ?? Str::upper($this->slug); } } From 42bfafb50a9b8f4a92230a1f62c0f98bc2e8da70 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Tue, 2 Nov 2021 20:50:32 +0300 Subject: [PATCH 04/51] [7.x] add uuid --- ...2018_11_15_124230_create_wallets_table.php | 1 + ...11_02_202021_update_wallets_uuid_table.php | 50 +++++++++++++++++++ src/Models/Wallet.php | 2 + src/Traits/HasWallets.php | 4 +- src/Traits/MorphOneWallet.php | 2 + tests/MultiWalletTest.php | 2 + 6 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 database/2021_11_02_202021_update_wallets_uuid_table.php diff --git a/database/2018_11_15_124230_create_wallets_table.php b/database/2018_11_15_124230_create_wallets_table.php index 821c25326..7658ffd24 100644 --- a/database/2018_11_15_124230_create_wallets_table.php +++ b/database/2018_11_15_124230_create_wallets_table.php @@ -17,6 +17,7 @@ public function up(): void $table->morphs('holder'); $table->string('name'); $table->string('slug')->index(); + $table->uuid('uuid')->unique(); $table->string('description')->nullable(); $table->json('meta')->nullable(); $table->decimal('balance', 64, 0)->default(0); diff --git a/database/2021_11_02_202021_update_wallets_uuid_table.php b/database/2021_11_02_202021_update_wallets_uuid_table.php new file mode 100644 index 000000000..0394446de --- /dev/null +++ b/database/2021_11_02_202021_update_wallets_uuid_table.php @@ -0,0 +1,50 @@ +table(), 'uuid')) { + return; + } + + // upgrade from 6.x + Schema::table($this->table(), function (Blueprint $table) { + $table->uuid('uuid') + ->after('slug') + ->nullable() + ->unique() + ; + }); + + Wallet::query()->chunk(10000, static function (Collection $wallets) { + $wallets->each(function (Wallet $wallet) { + $wallet->uuid = app(UuidInterface::class)->uuid4(); + $wallet->save(); + }); + }); + + Schema::table($this->table(), static function (Blueprint $table) { + $table->uuid('uuid')->change(); + }); + } + + public function down(): void + { + Schema::dropColumns($this->table(), ['uuid']); + } + + protected function table(): string + { + return (new Wallet())->getTable(); + } +} diff --git a/src/Models/Wallet.php b/src/Models/Wallet.php index 00047186b..2bc197106 100644 --- a/src/Models/Wallet.php +++ b/src/Models/Wallet.php @@ -28,6 +28,7 @@ * @property int $holder_id * @property string $name * @property string $slug + * @property string $uuid * @property string $description * @property array $meta * @property int $decimal_places @@ -49,6 +50,7 @@ class Wallet extends Model implements Customer, WalletFloat, Confirmable, Exchan 'holder_id', 'name', 'slug', + 'uuid', 'description', 'meta', 'balance', diff --git a/src/Traits/HasWallets.php b/src/Traits/HasWallets.php index 28bee7ef3..1fc382ad3 100644 --- a/src/Traits/HasWallets.php +++ b/src/Traits/HasWallets.php @@ -3,6 +3,7 @@ namespace Bavix\Wallet\Traits; use function array_key_exists; +use Bavix\Wallet\Internal\UuidInterface; use Bavix\Wallet\Models\Wallet as WalletModel; use function config; use Illuminate\Database\Eloquent\ModelNotFoundException; @@ -94,7 +95,8 @@ public function createWallet(array $data): WalletModel /** @var WalletModel $wallet */ $wallet = $this->wallets()->create(array_merge( config('wallet.wallet.creating', []), - $data + $data, + ['uuid' => app(UuidInterface::class)->uuid4()] )); $this->_wallets[$wallet->slug] = $wallet; diff --git a/src/Traits/MorphOneWallet.php b/src/Traits/MorphOneWallet.php index 531ce5932..ecc92378d 100644 --- a/src/Traits/MorphOneWallet.php +++ b/src/Traits/MorphOneWallet.php @@ -2,6 +2,7 @@ namespace Bavix\Wallet\Traits; +use Bavix\Wallet\Internal\UuidInterface; use Bavix\Wallet\Models\Wallet as WalletModel; use Illuminate\Database\Eloquent\Relations\MorphOne; @@ -25,6 +26,7 @@ public function wallet(): MorphOne 'name' => config('wallet.wallet.default.name', 'Default Wallet'), 'slug' => config('wallet.wallet.default.slug', 'default'), 'meta' => config('wallet.wallet.default.meta', []), + 'uuid' => app(UuidInterface::class)->uuid4(), 'balance' => 0, ])) ; diff --git a/tests/MultiWalletTest.php b/tests/MultiWalletTest.php index e8c2678af..2feeec016 100644 --- a/tests/MultiWalletTest.php +++ b/tests/MultiWalletTest.php @@ -4,6 +4,7 @@ use Bavix\Wallet\Exceptions\AmountInvalid; use Bavix\Wallet\Exceptions\BalanceIsEmpty; +use Bavix\Wallet\Internal\UuidInterface; use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Services\DbService; @@ -395,6 +396,7 @@ public function testGetWallet(): void $test2 = $user->wallets()->create([ 'name' => 'Test2', + 'uuid' => app(UuidInterface::class)->uuid4(), ]); self::assertEquals( From 78f4a4eb40fc1661f28631a94f693acb68cfd664 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Tue, 2 Nov 2021 21:53:48 +0300 Subject: [PATCH 05/51] [7.x] A lot of work on performance --- composer.json | 3 ++- src/Objects/Bring.php | 3 +++ src/Objects/Operation.php | 12 +++++++++++- src/Services/CommonService.php | 27 ++++++++++++++++++++++----- tests/FilterTest.php | 6 ------ 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index 9ba30e623..4f6965028 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,8 @@ "illuminate/database": "^6.0|^7.0|^8.0", "doctrine/dbal": "^2.8|^3.0", "ramsey/uuid": "^3.0|^4.0", - "brick/math": "~0.8" + "brick/math": "~0.8", + "ext-json": "*" }, "require-dev": { "brianium/paratest": "^6.2", diff --git a/src/Objects/Bring.php b/src/Objects/Bring.php index 4ea885725..c9aefe9f1 100644 --- a/src/Objects/Bring.php +++ b/src/Objects/Bring.php @@ -7,6 +7,7 @@ use Bavix\Wallet\Internal\UuidInterface; use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; +use DateTimeImmutable; /** @deprecated There is no alternative yet, but the class will be removed */ class Bring @@ -209,6 +210,8 @@ public function toArray(): array 'discount' => $this->getDiscount(), 'fee' => $this->getFee(), 'uuid' => $this->getUuid(), + 'created_at' => new DateTimeImmutable(), + 'updated_at' => new DateTimeImmutable(), ]; } } diff --git a/src/Objects/Operation.php b/src/Objects/Operation.php index 84bccb160..115abddbe 100644 --- a/src/Objects/Operation.php +++ b/src/Objects/Operation.php @@ -6,6 +6,8 @@ use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Internal\UuidInterface; use Bavix\Wallet\Models\Transaction; +use Bavix\Wallet\Models\Wallet as WalletModel; +use DateTimeImmutable; /** @deprecated There is no alternative yet, but the class will be removed */ class Operation @@ -151,13 +153,21 @@ public function create(): Transaction */ public function toArray(): array { + $wallet = $this->getWallet(); + $payable = $wallet instanceof WalletModel ? $wallet->holder : $wallet; + $meta = $this->getMeta(); + return [ 'type' => $this->getType(), + 'payable_type' => $payable->getMorphClass(), + 'payable_id' => $payable->getKey(), 'wallet_id' => $this->getWallet()->getKey(), 'uuid' => $this->getUuid(), 'confirmed' => $this->isConfirmed(), 'amount' => $this->getAmount(), - 'meta' => $this->getMeta(), + 'meta' => $meta === null ? null : json_encode($meta, JSON_THROW_ON_ERROR), + 'created_at' => new DateTimeImmutable(), + 'updated_at' => new DateTimeImmutable(), ]; } } diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 8ad16c3d0..7acc181b6 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -162,15 +162,20 @@ public function multiOperation(Wallet $self, array $operations): array $amount = $this->math->add($amount, $operation->getAmount()); } - $objects[] = $operation + $objects[$operation->getUuid()] = $operation ->setWallet($self) - ->create() + ->toArray() ; } + $model = app(config('wallet.transaction.model', Transaction::class)); + $model->insert(array_values($objects)); $this->addBalance($self, $amount); - return $objects; + return $model->query() + ->where('uuid', array_keys($objects)) + ->get() + ->all(); }); } @@ -197,17 +202,29 @@ public function assemble(array $brings): array /** * Create Bring without DB::transaction. * + * @param Bring[] $brings + * * @deprecated */ public function multiBrings(array $brings): array { + if (count($brings) === 1) { + return [current($brings)->create()]; + } + return $this->lockService->lock($this, __FUNCTION__, function () use ($brings) { $objects = []; foreach ($brings as $bring) { - $objects[] = $bring->create(); + $objects[$bring->getUuid()] = $bring->toArray(); } - return $objects; + $model = app(config('wallet.transfer.model', Transfer::class)); + $model->insert(array_values($objects)); + + return $model->query() + ->where('uuid', array_keys($objects)) + ->get() + ->all(); }); } diff --git a/tests/FilterTest.php b/tests/FilterTest.php index c852a9a49..e82004639 100644 --- a/tests/FilterTest.php +++ b/tests/FilterTest.php @@ -23,12 +23,6 @@ public function testMetaAccount(): void self::assertEquals(4, $buyer->transactions()->count()); - if (version_compare(PHP_VERSION, '7.3.0') < 0) { - self::markTestSkipped('You are using old php. Test not available.'); - - return; - } - $customers = $buyer->transactions()->where('meta->account', 'customers')->count(); $expenses = $buyer->transactions()->where('meta->account', 'expenses')->count(); $vendors = $buyer->transactions()->where('meta->account', 'vendors')->count(); From 21fdc4a01659869585e95b4a669a907bb07dd32d Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Tue, 2 Nov 2021 21:56:45 +0300 Subject: [PATCH 06/51] ecs-fix --- src/Services/CommonService.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 7acc181b6..e61063cdb 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -175,7 +175,8 @@ public function multiOperation(Wallet $self, array $operations): array return $model->query() ->where('uuid', array_keys($objects)) ->get() - ->all(); + ->all() + ; }); } @@ -224,7 +225,8 @@ public function multiBrings(array $brings): array return $model->query() ->where('uuid', array_keys($objects)) ->get() - ->all(); + ->all() + ; }); } From 11feda858fcdebc5e9354d78262a6851efcc72f6 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Tue, 2 Nov 2021 21:59:04 +0300 Subject: [PATCH 07/51] update config.php --- .phpstorm.meta.php | 1 - config/config.php | 6 ------ 2 files changed, 7 deletions(-) diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index ea89c12b3..b64e31712 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -13,7 +13,6 @@ use Bavix\Wallet\Objects\EmptyLock; use Bavix\Wallet\Objects\Operation; use Bavix\Wallet\Services\CommonService; - use Bavix\Wallet\Services\ExchangeService; use Bavix\Wallet\Services\WalletService; override(\app(0), map([ diff --git a/config/config.php b/config/config.php index 8f438a2b6..1895d7906 100644 --- a/config/config.php +++ b/config/config.php @@ -5,16 +5,12 @@ use Bavix\Wallet\Models\Wallet; use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Objects\Cart; -use Bavix\Wallet\Objects\EmptyLock; use Bavix\Wallet\Objects\Operation; use Bavix\Wallet\Services\CommonService; -use Bavix\Wallet\Services\ExchangeService; use Bavix\Wallet\Services\LockService; use Bavix\Wallet\Services\MathService; use Bavix\Wallet\Services\WalletService; use Bavix\Wallet\Simple\Exchange; -use Bavix\Wallet\Simple\Rate; -use Bavix\Wallet\Simple\Store; return [ /** @@ -55,7 +51,6 @@ * This configuration will help you to quickly customize the library. */ 'services' => [ - 'exchange' => ExchangeService::class, 'common' => CommonService::class, 'wallet' => WalletService::class, 'lock' => LockService::class, @@ -64,7 +59,6 @@ 'objects' => [ 'bring' => Bring::class, 'cart' => Cart::class, - 'emptyLock' => EmptyLock::class, 'operation' => Operation::class, ], From ad884c1aae5e6b5b8995689040a967b866f4db91 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Tue, 2 Nov 2021 22:03:11 +0300 Subject: [PATCH 08/51] let's slow down a little --- src/Services/CommonService.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index e61063cdb..af5266b3c 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -209,10 +209,6 @@ public function assemble(array $brings): array */ public function multiBrings(array $brings): array { - if (count($brings) === 1) { - return [current($brings)->create()]; - } - return $this->lockService->lock($this, __FUNCTION__, function () use ($brings) { $objects = []; foreach ($brings as $bring) { From 4f4b0dda3b72952df8bc840200422204ebd94d55 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Tue, 2 Nov 2021 23:38:29 +0300 Subject: [PATCH 09/51] add transfer dto --- .../Assembler/TransferDtoAssembler.php | 42 +++++++ src/Internal/Dto/TransferDto.php | 115 ++++++++++++++++++ src/Internal/Query/TransferQuery.php | 22 ++++ .../Repository/TransferRepository.php | 51 ++++++++ .../Transform/TransferDtoTransformer.php | 28 +++++ src/Objects/Bring.php | 33 ----- src/Objects/Operation.php | 11 -- src/Services/CommonService.php | 21 ++-- 8 files changed, 270 insertions(+), 53 deletions(-) create mode 100644 src/Internal/Assembler/TransferDtoAssembler.php create mode 100644 src/Internal/Dto/TransferDto.php create mode 100644 src/Internal/Query/TransferQuery.php create mode 100644 src/Internal/Repository/TransferRepository.php create mode 100644 src/Internal/Transform/TransferDtoTransformer.php diff --git a/src/Internal/Assembler/TransferDtoAssembler.php b/src/Internal/Assembler/TransferDtoAssembler.php new file mode 100644 index 000000000..8be776414 --- /dev/null +++ b/src/Internal/Assembler/TransferDtoAssembler.php @@ -0,0 +1,42 @@ +uuidService = $uuidService; + } + + public function create( + int $depositId, + int $withdrawId, + string $status, + Model $fromModel, + Model $toModel, + int $discount, + int $fee + ): TransferDto { + return new TransferDto( + $this->uuidService->uuid4(), + $depositId, + $withdrawId, + $status, + $fromModel->getMorphClass(), + $fromModel->getKey(), + $toModel->getMorphClass(), + $toModel->getKey(), + $discount, + $fee + ); + } +} diff --git a/src/Internal/Dto/TransferDto.php b/src/Internal/Dto/TransferDto.php new file mode 100644 index 000000000..f92cb9cfd --- /dev/null +++ b/src/Internal/Dto/TransferDto.php @@ -0,0 +1,115 @@ +uuid = $uuid; + $this->depositId = $depositId; + $this->withdrawId = $withdrawId; + $this->status = $status; + $this->fromType = $fromType; + $this->fromId = $fromId; + $this->toType = $toType; + $this->toId = $toId; + $this->discount = $discount; + $this->fee = $fee; + $this->createdAt = new DateTimeImmutable(); + $this->updatedAt = new DateTimeImmutable(); + } + + public function getUuid(): string + { + return $this->uuid; + } + + public function getDepositId(): int + { + return $this->depositId; + } + + public function getWithdrawId(): int + { + return $this->withdrawId; + } + + public function getStatus(): string + { + return $this->status; + } + + public function getFromType(): string + { + return $this->fromType; + } + + public function getFromId(): int + { + return $this->fromId; + } + + public function getToType(): string + { + return $this->toType; + } + + public function getToId(): int + { + return $this->toId; + } + + public function getDiscount(): int + { + return $this->discount; + } + + public function getFee(): int + { + return $this->fee; + } + + public function getCreatedAt(): DateTimeImmutable + { + return $this->createdAt; + } + + public function getUpdatedAt(): DateTimeImmutable + { + return $this->updatedAt; + } +} diff --git a/src/Internal/Query/TransferQuery.php b/src/Internal/Query/TransferQuery.php new file mode 100644 index 000000000..8e3953457 --- /dev/null +++ b/src/Internal/Query/TransferQuery.php @@ -0,0 +1,22 @@ +uuids = $uuids; + } + + /** @return string[] */ + public function getUuids(): array + { + return $this->uuids; + } +} diff --git a/src/Internal/Repository/TransferRepository.php b/src/Internal/Repository/TransferRepository.php new file mode 100644 index 000000000..5077f51d8 --- /dev/null +++ b/src/Internal/Repository/TransferRepository.php @@ -0,0 +1,51 @@ +transformer = $transformer; + $this->transfer = $transfer; + } + + /** + * @param TransferDto[] $transfers + * + * @return Transfer[] + */ + public function insert(array $transfers): array + { + $values = array_map(fn (TransferDto $dto): array => $this->transformer->extract($dto), $transfers); + $this->transfer->newQuery()->insert($values); + + $uuids = array_map(static fn (TransferDto $dto): string => $dto->getUuid(), $transfers); + $query = new TransferQuery($uuids); + + return $this->findBy($query); + } + + /** @return Transfer[] */ + public function findBy(TransferQuery $transferQuery): array + { + return $this->transfer->newQuery() + ->where('uuid', $transferQuery->getUuids()) + ->get() + ->all() + ; + } +} diff --git a/src/Internal/Transform/TransferDtoTransformer.php b/src/Internal/Transform/TransferDtoTransformer.php new file mode 100644 index 000000000..f27622237 --- /dev/null +++ b/src/Internal/Transform/TransferDtoTransformer.php @@ -0,0 +1,28 @@ + $dto->getUuid(), + 'deposit_id' => $dto->getDepositId(), + 'withdraw_id' => $dto->getWithdrawId(), + 'status' => $dto->getStatus(), + 'from_type' => $dto->getFromType(), + 'from_id' => $dto->getFromId(), + 'to_type' => $dto->getToType(), + 'to_id' => $dto->getToId(), + 'discount' => $dto->getDiscount(), + 'fee' => $dto->getFee(), + 'created_at' => $dto->getCreatedAt(), + 'updated_at' => $dto->getUpdatedAt(), + ]; + } +} diff --git a/src/Objects/Bring.php b/src/Objects/Bring.php index c9aefe9f1..e67dcd3b1 100644 --- a/src/Objects/Bring.php +++ b/src/Objects/Bring.php @@ -6,8 +6,6 @@ use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Internal\UuidInterface; use Bavix\Wallet\Models\Transaction; -use Bavix\Wallet\Models\Transfer; -use DateTimeImmutable; /** @deprecated There is no alternative yet, but the class will be removed */ class Bring @@ -183,35 +181,4 @@ public function setFee($fee): self return $this; } - - /** - * @throws - */ - public function create(): Transfer - { - return app(Transfer::class) - ->create($this->toArray()) - ; - } - - /** - * @throws - */ - public function toArray(): array - { - return [ - 'status' => $this->getStatus(), - 'deposit_id' => $this->getDeposit()->getKey(), - 'withdraw_id' => $this->getWithdraw()->getKey(), - 'from_type' => $this->getFrom()->getMorphClass(), - 'from_id' => $this->getFrom()->getKey(), - 'to_type' => $this->getTo()->getMorphClass(), - 'to_id' => $this->getTo()->getKey(), - 'discount' => $this->getDiscount(), - 'fee' => $this->getFee(), - 'uuid' => $this->getUuid(), - 'created_at' => new DateTimeImmutable(), - 'updated_at' => new DateTimeImmutable(), - ]; - } } diff --git a/src/Objects/Operation.php b/src/Objects/Operation.php index 115abddbe..e45c5659a 100644 --- a/src/Objects/Operation.php +++ b/src/Objects/Operation.php @@ -137,17 +137,6 @@ public function setWallet(Wallet $wallet): self return $this; } - public function create(): Transaction - { - /** - * @var Transaction $model - */ - return $this->getWallet() - ->transactions() - ->create($this->toArray()) - ; - } - /** * @throws */ diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index af5266b3c..067e7d4c1 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -7,9 +7,11 @@ use Bavix\Wallet\Exceptions\BalanceIsEmpty; use Bavix\Wallet\Exceptions\InsufficientFunds; use Bavix\Wallet\Interfaces\Wallet; +use Bavix\Wallet\Internal\Assembler\TransferDtoAssembler; use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Internal\Repository\TransferRepository; use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Models\Wallet as WalletModel; @@ -212,17 +214,18 @@ public function multiBrings(array $brings): array return $this->lockService->lock($this, __FUNCTION__, function () use ($brings) { $objects = []; foreach ($brings as $bring) { - $objects[$bring->getUuid()] = $bring->toArray(); + $objects[$bring->getUuid()] = app(TransferDtoAssembler::class)->create( + $bring->getDeposit()->getKey(), + $bring->getWithdraw()->getKey(), + $bring->getStatus(), + $bring->getFrom(), + $bring->getTo(), + $bring->getDiscount(), + $bring->getFee() + ); } - $model = app(config('wallet.transfer.model', Transfer::class)); - $model->insert(array_values($objects)); - - return $model->query() - ->where('uuid', array_keys($objects)) - ->get() - ->all() - ; + return app(TransferRepository::class)->insert($objects); }); } From c4a155972b0c65e0f14d8517d1e1d07a6657f9a8 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 3 Nov 2021 21:19:05 +0300 Subject: [PATCH 10/51] update cart object --- src/Objects/Cart.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Objects/Cart.php b/src/Objects/Cart.php index 2095e9c28..3147f3238 100644 --- a/src/Objects/Cart.php +++ b/src/Objects/Cart.php @@ -7,7 +7,6 @@ use function array_unique; use Bavix\Wallet\Interfaces\Customer; use Bavix\Wallet\Interfaces\Product; -use Bavix\Wallet\Internal\BasketInterface; use Bavix\Wallet\Internal\CartInterface; use Bavix\Wallet\Internal\Dto\BasketDto; use Bavix\Wallet\Internal\Dto\ItemDto; @@ -29,15 +28,11 @@ class Cart implements Countable, CartInterface private array $meta = []; - private BasketInterface $basket; - private MathInterface $math; public function __construct( - BasketInterface $basket, MathInterface $math ) { - $this->basket = $basket; $this->math = $math; } From 90a644b280125848422beac32dd436a7c352299f Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 3 Nov 2021 23:14:12 +0300 Subject: [PATCH 11/51] For a very long time, but I did it. Meet psalm --- .github/workflows/code-style.yaml | 2 +- .github/workflows/psalm.yaml | 38 +++++++++++++++++++++++ src/Interfaces/Wallet.php | 2 ++ src/Internal/ConsistencyInterface.php | 2 +- src/Internal/Service/CastService.php | 44 +++++++++++++++++++++++++++ src/Objects/Cart.php | 2 +- src/Objects/Operation.php | 13 +++++--- src/Services/ConsistencyService.php | 18 ++++++----- src/Services/StorageService.php | 2 +- src/Services/WalletService.php | 18 ++--------- src/Traits/HasWallet.php | 4 ++- src/Traits/HasWalletFloat.php | 4 +-- src/Traits/MorphOneWallet.php | 4 ++- src/WalletServiceProvider.php | 2 -- 14 files changed, 118 insertions(+), 37 deletions(-) create mode 100644 .github/workflows/psalm.yaml create mode 100644 src/Internal/Service/CastService.php diff --git a/.github/workflows/code-style.yaml b/.github/workflows/code-style.yaml index 20403c0ad..db8160685 100644 --- a/.github/workflows/code-style.yaml +++ b/.github/workflows/code-style.yaml @@ -17,7 +17,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 7.4 + php-version: 8.0 - name: Validate composer.json and composer.lock run: composer validate --strict diff --git a/.github/workflows/psalm.yaml b/.github/workflows/psalm.yaml new file mode 100644 index 000000000..a1e25d978 --- /dev/null +++ b/.github/workflows/psalm.yaml @@ -0,0 +1,38 @@ +name: psalm + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + psalm: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.0 + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v2 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install dependencies + run: composer install --prefer-dist --no-progress + + - name: Run psalm + run: composer psalm diff --git a/src/Interfaces/Wallet.php b/src/Interfaces/Wallet.php index 3b943dd77..334af6252 100644 --- a/src/Interfaces/Wallet.php +++ b/src/Interfaces/Wallet.php @@ -68,4 +68,6 @@ public function canWithdraw($amount, bool $allowZero = false): bool; public function getBalanceAttribute(); public function transactions(): MorphMany; + + public function transfers(): MorphMany; } diff --git a/src/Internal/ConsistencyInterface.php b/src/Internal/ConsistencyInterface.php index 8b5dcf374..a10110314 100644 --- a/src/Internal/ConsistencyInterface.php +++ b/src/Internal/ConsistencyInterface.php @@ -24,5 +24,5 @@ public function checkPositive($amount): void; * @throws BalanceIsEmpty * @throws InsufficientFunds */ - public function checkPotential(Wallet $wallet, $amount, bool $allowZero = false): void; + public function checkPotential(Wallet $object, $amount, bool $allowZero = false): void; } diff --git a/src/Internal/Service/CastService.php b/src/Internal/Service/CastService.php new file mode 100644 index 000000000..bea3173e1 --- /dev/null +++ b/src/Internal/Service/CastService.php @@ -0,0 +1,44 @@ +getAttribute('wallet'); + } + + if ($save) { + $wallet->exists or $wallet->save(); + } + + return $wallet; + } + + /** @param Model|Wallet $object */ + public function getHolder($object): Model + { + assert($object instanceof Model); + if ($object instanceof WalletModel) { + assert($object->holder instanceof Model); + + return $object->holder; + } + + return $object; + } +} diff --git a/src/Objects/Cart.php b/src/Objects/Cart.php index 3147f3238..8ec21c4d3 100644 --- a/src/Objects/Cart.php +++ b/src/Objects/Cart.php @@ -23,7 +23,7 @@ class Cart implements Countable, CartInterface */ private array $items = []; - /** @var array */ + /** @var array */ private array $quantity = []; private array $meta = []; diff --git a/src/Objects/Operation.php b/src/Objects/Operation.php index e45c5659a..3e066cbaf 100644 --- a/src/Objects/Operation.php +++ b/src/Objects/Operation.php @@ -4,9 +4,9 @@ use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Internal\Service\CastService; use Bavix\Wallet\Internal\UuidInterface; use Bavix\Wallet\Models\Transaction; -use Bavix\Wallet\Models\Wallet as WalletModel; use DateTimeImmutable; /** @deprecated There is no alternative yet, but the class will be removed */ @@ -42,14 +42,17 @@ class Operation */ protected $wallet; + private CastService $castService; + /** * Transaction constructor. * * @throws */ - public function __construct(UuidInterface $uuidService) + public function __construct(UuidInterface $uuidService, CastService $castService) { $this->uuid = $uuidService->uuid4(); + $this->castService = $castService; } public function getType(): string @@ -142,15 +145,15 @@ public function setWallet(Wallet $wallet): self */ public function toArray(): array { - $wallet = $this->getWallet(); - $payable = $wallet instanceof WalletModel ? $wallet->holder : $wallet; + $wallet = $this->castService->getWallet($this->getWallet()); + $payable = $this->castService->getHolder($wallet); $meta = $this->getMeta(); return [ 'type' => $this->getType(), 'payable_type' => $payable->getMorphClass(), 'payable_id' => $payable->getKey(), - 'wallet_id' => $this->getWallet()->getKey(), + 'wallet_id' => $wallet->getKey(), 'uuid' => $this->getUuid(), 'confirmed' => $this->isConfirmed(), 'amount' => $this->getAmount(), diff --git a/src/Services/ConsistencyService.php b/src/Services/ConsistencyService.php index d68bb8f7a..76b1f4931 100644 --- a/src/Services/ConsistencyService.php +++ b/src/Services/ConsistencyService.php @@ -10,15 +10,20 @@ use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\MathInterface; -use Bavix\Wallet\Traits\HasWallet; +use Bavix\Wallet\Internal\Service\CastService; class ConsistencyService implements ConsistencyInterface { + private CastService $castService; + private MathInterface $math; - public function __construct(MathInterface $math) - { + public function __construct( + MathInterface $math, + CastService $castService + ) { $this->math = $math; + $this->castService = $castService; } /** @@ -39,11 +44,10 @@ public function checkPositive($amount): void * @throws BalanceIsEmpty * @throws InsufficientFunds */ - public function checkPotential(Wallet $wallet, $amount, bool $allowZero = false): void + public function checkPotential(Wallet $object, $amount, bool $allowZero = false): void { - /** - * @var HasWallet $wallet - */ + $wallet = $this->castService->getWallet($object, false); + if ($amount && !$wallet->balance) { throw new BalanceIsEmpty(trans('wallet::errors.wallet_empty')); } diff --git a/src/Services/StorageService.php b/src/Services/StorageService.php index efd26af3c..eedfba4c3 100644 --- a/src/Services/StorageService.php +++ b/src/Services/StorageService.php @@ -35,7 +35,7 @@ public function __construct( public function flush(): bool { - return $this->cache->flush(); + return $this->cache->clear(); } public function missing(string $key): bool diff --git a/src/Services/WalletService.php b/src/Services/WalletService.php index c855d121f..5ba3a9c9e 100644 --- a/src/Services/WalletService.php +++ b/src/Services/WalletService.php @@ -10,8 +10,8 @@ use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Internal\Service\CastService; use Bavix\Wallet\Models\Wallet as WalletModel; -use Bavix\Wallet\Traits\HasWallet; use Throwable; class WalletService @@ -54,7 +54,7 @@ public function discount(Wallet $customer, Wallet $product): int * * @param int|string $amount * - * @return float|int + * @return float|int|string */ public function fee(Wallet $wallet, $amount) { @@ -86,19 +86,7 @@ public function fee(Wallet $wallet, $amount) public function getWallet(Wallet $object, bool $autoSave = true): WalletModel { - /** @var WalletModel $wallet */ - $wallet = $object; - - if (!($object instanceof WalletModel)) { - /** @var HasWallet $object */ - $wallet = $object->wallet; - } - - if ($autoSave) { - $wallet->exists or $wallet->save(); - } - - return $wallet; + return app(CastService::class)->getWallet($object, $autoSave); } /** diff --git a/src/Traits/HasWallet.php b/src/Traits/HasWallet.php index 9175c54f4..d5a901997 100644 --- a/src/Traits/HasWallet.php +++ b/src/Traits/HasWallet.php @@ -10,6 +10,7 @@ use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Internal\Service\CastService; use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Models\Wallet as WalletModel; @@ -98,7 +99,8 @@ public function getBalanceIntAttribute(): int */ public function transactions(): MorphMany { - return ($this instanceof WalletModel ? $this->holder : $this) + return app(CastService::class) + ->getHolder($this) ->morphMany(config('wallet.transaction.model', Transaction::class), 'payable') ; } diff --git a/src/Traits/HasWalletFloat.php b/src/Traits/HasWalletFloat.php index 1096bff00..a162cac97 100644 --- a/src/Traits/HasWalletFloat.php +++ b/src/Traits/HasWalletFloat.php @@ -89,7 +89,7 @@ public function canWithdrawFloat($amount): bool } /** - * @param float $amount + * @param float|string $amount * * @throws AmountInvalid * @throws BalanceIsEmpty @@ -108,7 +108,7 @@ public function transferFloat(Wallet $wallet, $amount, ?array $meta = null): Tra } /** - * @param float $amount + * @param float|string $amount */ public function safeTransferFloat(Wallet $wallet, $amount, ?array $meta = null): ?Transfer { diff --git a/src/Traits/MorphOneWallet.php b/src/Traits/MorphOneWallet.php index ecc92378d..a566ba795 100644 --- a/src/Traits/MorphOneWallet.php +++ b/src/Traits/MorphOneWallet.php @@ -2,6 +2,7 @@ namespace Bavix\Wallet\Traits; +use Bavix\Wallet\Internal\Service\CastService; use Bavix\Wallet\Internal\UuidInterface; use Bavix\Wallet\Models\Wallet as WalletModel; use Illuminate\Database\Eloquent\Relations\MorphOne; @@ -19,7 +20,8 @@ trait MorphOneWallet */ public function wallet(): MorphOne { - return ($this instanceof WalletModel ? $this->holder : $this) + return app(CastService::class) + ->getHolder($this) ->morphOne(config('wallet.wallet.model', WalletModel::class), 'holder') ->where('slug', config('wallet.wallet.default.slug', 'default')) ->withDefault(array_merge(config('wallet.wallet.creating', []), [ diff --git a/src/WalletServiceProvider.php b/src/WalletServiceProvider.php index 330322689..33b90885b 100644 --- a/src/WalletServiceProvider.php +++ b/src/WalletServiceProvider.php @@ -19,7 +19,6 @@ use Bavix\Wallet\Models\Wallet; use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Objects\Cart; -use Bavix\Wallet\Objects\EmptyLock; use Bavix\Wallet\Objects\Operation; use Bavix\Wallet\Services\AtomicService; use Bavix\Wallet\Services\BasketService; @@ -132,7 +131,6 @@ private function bindObjects(): void // object's $this->app->bind(Bring::class, config('wallet.objects.bring', Bring::class)); $this->app->bind(Cart::class, config('wallet.objects.cart', Cart::class)); - $this->app->bind(EmptyLock::class, config('wallet.objects.emptyLock', EmptyLock::class)); $this->app->bind(Operation::class, config('wallet.objects.operation', Operation::class)); } } From 1bc9dee190115d4f3f7cbd66960434ac3fc77180 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 3 Nov 2021 23:33:08 +0300 Subject: [PATCH 12/51] implement phpstan... --- src/Objects/Bring.php | 7 +------ src/Services/MathService.php | 2 +- src/Services/PurchaseService.php | 3 --- src/Traits/HasWalletFloat.php | 6 ++++-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/Objects/Bring.php b/src/Objects/Bring.php index e67dcd3b1..71e320051 100644 --- a/src/Objects/Bring.php +++ b/src/Objects/Bring.php @@ -41,7 +41,7 @@ class Bring protected $uuid; /** - * @var int + * @var null|int */ protected $fee; @@ -50,11 +50,6 @@ class Bring */ protected $discount; - /** - * Bring constructor. - * - * @throws - */ public function __construct(UuidInterface $uuidService) { $this->uuid = $uuidService->uuid4(); diff --git a/src/Services/MathService.php b/src/Services/MathService.php index 7f515d34b..8671b7d26 100644 --- a/src/Services/MathService.php +++ b/src/Services/MathService.php @@ -67,7 +67,7 @@ public function mul($first, $second, ?int $scale = null): string public function pow($first, $second, ?int $scale = null): string { return (string) BigDecimal::of($first) - ->power($second) + ->power((int) $second) ->toScale($scale ?? $this->scale, RoundingMode::DOWN) ; } diff --git a/src/Services/PurchaseService.php b/src/Services/PurchaseService.php index 1d0a8733e..4874961ec 100644 --- a/src/Services/PurchaseService.php +++ b/src/Services/PurchaseService.php @@ -8,7 +8,6 @@ use Bavix\Wallet\Internal\Dto\BasketDto; use Bavix\Wallet\Internal\PurchaseInterface; use Bavix\Wallet\Models\Transfer; -use Bavix\Wallet\Traits\HasWallet; use Illuminate\Database\Eloquent\Model; class PurchaseService implements PurchaseInterface @@ -19,8 +18,6 @@ public function already(Customer $customer, BasketDto $basketDto, bool $gifts = ? [Transfer::STATUS_PAID, Transfer::STATUS_GIFT] : [Transfer::STATUS_PAID]; - /** @var HasWallet $customer */ - /** @var Transfer $query */ $arrays = []; $query = $customer->transfers(); foreach ($basketDto->items() as $itemDto) { diff --git a/src/Traits/HasWalletFloat.php b/src/Traits/HasWalletFloat.php index a162cac97..d217e74c7 100644 --- a/src/Traits/HasWalletFloat.php +++ b/src/Traits/HasWalletFloat.php @@ -7,6 +7,7 @@ use Bavix\Wallet\Exceptions\InsufficientFunds; use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Internal\Service\CastService; use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Services\WalletService; @@ -145,9 +146,10 @@ public function getBalanceFloatAttribute() { /** @var Wallet $this */ $math = app(MathInterface::class); - $decimalPlacesValue = app(WalletService::class)->getWallet($this)->decimal_places; + $wallet = app(CastService::class)->getWallet($this); + $decimalPlacesValue = $wallet->decimal_places; $decimalPlaces = $math->powTen($decimalPlacesValue); - return $math->div($this->balance, $decimalPlaces, $decimalPlacesValue); + return $math->div($wallet->balance, $decimalPlaces, $decimalPlacesValue); } } From f3417fdef93cd52bf76fa363de1dad3511f5f61e Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 3 Nov 2021 23:44:38 +0300 Subject: [PATCH 13/51] drop @throws --- src/Commands/RefreshBalance.php | 3 --- src/Interfaces/Customer.php | 21 --------------------- src/Objects/Operation.php | 5 ----- src/Services/CommonService.php | 4 ---- src/Services/LockService.php | 2 -- src/Traits/CanPay.php | 21 --------------------- src/Traits/CartPay.php | 18 ------------------ 7 files changed, 74 deletions(-) diff --git a/src/Commands/RefreshBalance.php b/src/Commands/RefreshBalance.php index 61774cb98..85df85982 100644 --- a/src/Commands/RefreshBalance.php +++ b/src/Commands/RefreshBalance.php @@ -24,9 +24,6 @@ class RefreshBalance extends Command */ protected $description = 'Recalculates all wallets'; - /** - * @throws - */ public function handle(): void { Wallet::query()->each(static fn (Wallet $wallet) => $wallet->refreshBalance()); diff --git a/src/Interfaces/Customer.php b/src/Interfaces/Customer.php index 3da120096..bd489b5dd 100644 --- a/src/Interfaces/Customer.php +++ b/src/Interfaces/Customer.php @@ -9,26 +9,14 @@ interface Customer extends Wallet { - /** - * @throws - */ public function pay(Product $product, bool $force = false): Transfer; - /** - * @throws - */ public function safePay(Product $product, bool $force = false): ?Transfer; - /** - * @throws - */ public function forcePay(Product $product): Transfer; public function paid(Product $product, bool $gifts = false): ?Transfer; - /** - * @throws - */ public function refund(Product $product, bool $force = false, bool $gifts = false): bool; public function safeRefund(Product $product, bool $force = false, bool $gifts = false): bool; @@ -36,29 +24,20 @@ public function safeRefund(Product $product, bool $force = false, bool $gifts = public function forceRefund(Product $product, bool $gifts = false): bool; /** - * @throws - * * @return Transfer[] */ public function payCart(CartInterface $cart, bool $force = false): array; /** - * @throws - * * @return Transfer[] */ public function safePayCart(CartInterface $cart, bool $force = false): array; /** - * @throws - * * @return Transfer[] */ public function forcePayCart(CartInterface $cart): array; - /** - * @throws - */ public function refundCart(CartInterface $cart, bool $force = false, bool $gifts = false): bool; public function safeRefundCart(CartInterface $cart, bool $force = false, bool $gifts = false): bool; diff --git a/src/Objects/Operation.php b/src/Objects/Operation.php index 3e066cbaf..32a6aad3d 100644 --- a/src/Objects/Operation.php +++ b/src/Objects/Operation.php @@ -46,8 +46,6 @@ class Operation /** * Transaction constructor. - * - * @throws */ public function __construct(UuidInterface $uuidService, CastService $castService) { @@ -140,9 +138,6 @@ public function setWallet(Wallet $wallet): self return $this; } - /** - * @throws - */ public function toArray(): array { $wallet = $this->castService->getWallet($this->getWallet()); diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 067e7d4c1..2c13945c2 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -187,8 +187,6 @@ public function multiOperation(Wallet $self, array $operations): array * * @param Bring[] $brings * - * @throws - * * @deprecated */ public function assemble(array $brings): array @@ -232,8 +230,6 @@ public function multiBrings(array $brings): array /** * @param int|string $amount * - * @throws - * * @deprecated */ public function addBalance(Wallet $wallet, $amount): bool diff --git a/src/Services/LockService.php b/src/Services/LockService.php index 4fdba64df..561ff91e3 100644 --- a/src/Services/LockService.php +++ b/src/Services/LockService.php @@ -47,8 +47,6 @@ public function lock($self, string $name, \Closure $closure) /** * @param object $self - * - * @throws */ protected function bindTo($self, \Closure $closure): \Closure { diff --git a/src/Traits/CanPay.php b/src/Traits/CanPay.php index bee9eb90e..837fdeff2 100644 --- a/src/Traits/CanPay.php +++ b/src/Traits/CanPay.php @@ -11,9 +11,6 @@ trait CanPay { use CartPay; - /** - * @throws - */ public function payFree(Product $product): Transfer { return current($this->payFreeCart(app(Cart::class)->addItem($product))); @@ -24,17 +21,11 @@ public function safePay(Product $product, bool $force = false): ?Transfer return current($this->safePayCart(app(Cart::class)->addItem($product), $force)) ?: null; } - /** - * @throws - */ public function pay(Product $product, bool $force = false): Transfer { return current($this->payCart(app(Cart::class)->addItem($product), $force)); } - /** - * @throws - */ public function forcePay(Product $product): Transfer { return current($this->forcePayCart(app(Cart::class)->addItem($product))); @@ -45,17 +36,11 @@ public function safeRefund(Product $product, bool $force = false, bool $gifts = return $this->safeRefundCart(app(Cart::class)->addItem($product), $force, $gifts); } - /** - * @throws - */ public function refund(Product $product, bool $force = false, bool $gifts = false): bool { return $this->refundCart(app(Cart::class)->addItem($product), $force, $gifts); } - /** - * @throws - */ public function forceRefund(Product $product, bool $gifts = false): bool { return $this->forceRefundCart(app(Cart::class)->addItem($product), $gifts); @@ -66,17 +51,11 @@ public function safeRefundGift(Product $product, bool $force = false): bool return $this->safeRefundGiftCart(app(Cart::class)->addItem($product), $force); } - /** - * @throws - */ public function refundGift(Product $product, bool $force = false): bool { return $this->refundGiftCart(app(Cart::class)->addItem($product), $force); } - /** - * @throws - */ public function forceRefundGift(Product $product): bool { return $this->forceRefundGiftCart(app(Cart::class)->addItem($product)); diff --git a/src/Traits/CartPay.php b/src/Traits/CartPay.php index 8dd97d7f3..1978d14be 100644 --- a/src/Traits/CartPay.php +++ b/src/Traits/CartPay.php @@ -24,8 +24,6 @@ trait CartPay use HasWallet; /** - * @throws - * * @return Transfer[] */ public function payFreeCart(CartInterface $cart): array @@ -68,8 +66,6 @@ public function safePayCart(CartInterface $cart, bool $force = false): array } /** - * @throws - * * @return Transfer[] */ public function payCart(CartInterface $cart, bool $force = false): array @@ -110,8 +106,6 @@ public function payCart(CartInterface $cart, bool $force = false): array } /** - * @throws - * * @return Transfer[] */ public function forcePayCart(CartInterface $cart): array @@ -128,9 +122,6 @@ public function safeRefundCart(CartInterface $cart, bool $force = false, bool $g } } - /** - * @throws - */ public function refundCart(CartInterface $cart, bool $force = false, bool $gifts = false): bool { $self = $this; @@ -174,9 +165,6 @@ public function refundCart(CartInterface $cart, bool $force = false, bool $gifts }); } - /** - * @throws - */ public function forceRefundCart(CartInterface $cart, bool $gifts = false): bool { return $this->refundCart($cart, true, $gifts); @@ -191,17 +179,11 @@ public function safeRefundGiftCart(CartInterface $cart, bool $force = false): bo } } - /** - * @throws - */ public function refundGiftCart(CartInterface $cart, bool $force = false): bool { return $this->refundCart($cart, $force, true); } - /** - * @throws - */ public function forceRefundGiftCart(CartInterface $cart): bool { return $this->refundGiftCart($cart, true); From 41c1ec2d8ff44f1da55d284eaa345c2390cb64a2 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 3 Nov 2021 23:53:27 +0300 Subject: [PATCH 14/51] update internal rules --- src/Internal/Dto/AvailabilityDto.php | 1 + src/Internal/Dto/BasketDto.php | 5 +++-- src/Internal/Dto/ItemDto.php | 1 + src/Internal/Dto/TransferDto.php | 1 + src/Internal/Query/TransferQuery.php | 8 +++++--- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Internal/Dto/AvailabilityDto.php b/src/Internal/Dto/AvailabilityDto.php index c4ff9be06..b8ec47804 100644 --- a/src/Internal/Dto/AvailabilityDto.php +++ b/src/Internal/Dto/AvailabilityDto.php @@ -6,6 +6,7 @@ use Bavix\Wallet\Interfaces\Customer; +/** @psalm-immutable */ class AvailabilityDto { private BasketDto $basketDto; diff --git a/src/Internal/Dto/BasketDto.php b/src/Internal/Dto/BasketDto.php index 014d3f286..d8340e3fc 100644 --- a/src/Internal/Dto/BasketDto.php +++ b/src/Internal/Dto/BasketDto.php @@ -6,14 +6,15 @@ use Countable; +/** @psalm-immutable */ class BasketDto implements Countable { - /** @var ItemDto[] */ + /** @var non-empty-array */ private array $items; private array $meta; - /** @param ItemDto[] $items */ + /** @param non-empty-array $items */ public function __construct(array $items, array $meta) { $this->items = $items; diff --git a/src/Internal/Dto/ItemDto.php b/src/Internal/Dto/ItemDto.php index 4c6c4e0fb..f5164fe4d 100644 --- a/src/Internal/Dto/ItemDto.php +++ b/src/Internal/Dto/ItemDto.php @@ -7,6 +7,7 @@ use Bavix\Wallet\Interfaces\Product; use Countable; +/** @psalm-immutable */ class ItemDto implements Countable { private Product $product; diff --git a/src/Internal/Dto/TransferDto.php b/src/Internal/Dto/TransferDto.php index f92cb9cfd..621441c32 100644 --- a/src/Internal/Dto/TransferDto.php +++ b/src/Internal/Dto/TransferDto.php @@ -6,6 +6,7 @@ use DateTimeImmutable; +/** @psalm-immutable */ class TransferDto { private string $uuid; diff --git a/src/Internal/Query/TransferQuery.php b/src/Internal/Query/TransferQuery.php index 8e3953457..46b8ba47d 100644 --- a/src/Internal/Query/TransferQuery.php +++ b/src/Internal/Query/TransferQuery.php @@ -4,17 +4,19 @@ namespace Bavix\Wallet\Internal\Query; -class TransferQuery +/** @psalm-immutable */ +final class TransferQuery { + /** @var non-empty-array */ private array $uuids; - /** @param string[] $uuids */ + /** @param non-empty-array $uuids */ public function __construct(array $uuids) { $this->uuids = $uuids; } - /** @return string[] */ + /** @return non-empty-array */ public function getUuids(): array { return $this->uuids; From 66f5fb634e763615d3777f20781ec03d3fd355c1 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 4 Nov 2021 00:18:27 +0300 Subject: [PATCH 15/51] non-empty-array --- .../Repository/TransferRepository.php | 4 +-- src/Internal/Service/CastService.php | 20 ++++++-------- src/Services/CommonService.php | 26 ++++++++++++------- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/Internal/Repository/TransferRepository.php b/src/Internal/Repository/TransferRepository.php index 5077f51d8..3e3d27366 100644 --- a/src/Internal/Repository/TransferRepository.php +++ b/src/Internal/Repository/TransferRepository.php @@ -24,9 +24,9 @@ public function __construct( } /** - * @param TransferDto[] $transfers + * @param non-empty-array $transfers * - * @return Transfer[] + * @return non-empty-array */ public function insert(array $transfers): array { diff --git a/src/Internal/Service/CastService.php b/src/Internal/Service/CastService.php index bea3173e1..d5678dc07 100644 --- a/src/Internal/Service/CastService.php +++ b/src/Internal/Service/CastService.php @@ -13,13 +13,9 @@ class CastService { public function getWallet(Wallet $object, bool $save = true): WalletModel { - assert($object instanceof Model); - - /** @var WalletModel $wallet */ - $wallet = $object; - - if (!($object instanceof WalletModel)) { - $wallet = $object->getAttribute('wallet'); + $wallet = $this->getModel($object); + if (!($wallet instanceof WalletModel)) { + $wallet = $wallet->getAttribute('wallet'); } if ($save) { @@ -32,12 +28,12 @@ public function getWallet(Wallet $object, bool $save = true): WalletModel /** @param Model|Wallet $object */ public function getHolder($object): Model { - assert($object instanceof Model); - if ($object instanceof WalletModel) { - assert($object->holder instanceof Model); + return $this->getModel($object instanceof WalletModel ? $object->holder : $object); + } - return $object->holder; - } + public function getModel(object $object): Model + { + assert($object instanceof Model); return $object; } diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 2c13945c2..70609c745 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -10,8 +10,10 @@ use Bavix\Wallet\Internal\Assembler\TransferDtoAssembler; use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; +use Bavix\Wallet\Internal\Dto\TransferDto; use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Internal\Repository\TransferRepository; +use Bavix\Wallet\Internal\Service\CastService; use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Models\Wallet as WalletModel; @@ -26,24 +28,30 @@ class CommonService private DbService $dbService; private LockService $lockService; private MathInterface $math; + private CastService $castService; private WalletService $walletService; private BookkeeperInterface $bookkeeper; private ConsistencyInterface $consistency; + private TransferDtoAssembler $transferDtoAssembler; public function __construct( DbService $dbService, LockService $lockService, MathInterface $math, + CastService $castService, WalletService $walletService, BookkeeperInterface $bookkeeper, - ConsistencyInterface $consistency + ConsistencyInterface $consistency, + TransferDtoAssembler $transferDtoAssembler ) { $this->dbService = $dbService; $this->lockService = $lockService; $this->math = $math; + $this->castService = $castService; $this->walletService = $walletService; $this->bookkeeper = $bookkeeper; $this->consistency = $consistency; + $this->transferDtoAssembler = $transferDtoAssembler; } /** @@ -203,25 +211,25 @@ public function assemble(array $brings): array /** * Create Bring without DB::transaction. * - * @param Bring[] $brings + * @param non-empty-array $brings * * @deprecated */ public function multiBrings(array $brings): array { return $this->lockService->lock($this, __FUNCTION__, function () use ($brings) { - $objects = []; - foreach ($brings as $bring) { - $objects[$bring->getUuid()] = app(TransferDtoAssembler::class)->create( + $objects = array_map( + fn (Bring $bring): TransferDto => $this->transferDtoAssembler->create( $bring->getDeposit()->getKey(), $bring->getWithdraw()->getKey(), $bring->getStatus(), - $bring->getFrom(), - $bring->getTo(), + $this->castService->getModel($bring->getFrom()), + $this->castService->getModel($bring->getTo()), $bring->getDiscount(), $bring->getFee() - ); - } + ), + $brings + ); return app(TransferRepository::class)->insert($objects); }); From a6b5b109ac1b290cb47ea8ce0866c2af5759a8a0 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 4 Nov 2021 00:27:44 +0300 Subject: [PATCH 16/51] drop getUuid --- src/Objects/Bring.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Objects/Bring.php b/src/Objects/Bring.php index 71e320051..65540dab9 100644 --- a/src/Objects/Bring.php +++ b/src/Objects/Bring.php @@ -140,11 +140,6 @@ public function setWithdraw(Transaction $withdraw): self return $this; } - public function getUuid(): string - { - return $this->uuid; - } - public function getDiscount(): int { return $this->discount; From 105864aa04cfbc6ef7cf2bb53e245ca8f2e20db3 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 4 Nov 2021 08:55:23 +0300 Subject: [PATCH 17/51] psalm --- src/Objects/Bring.php | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/Objects/Bring.php b/src/Objects/Bring.php index 65540dab9..00030be2b 100644 --- a/src/Objects/Bring.php +++ b/src/Objects/Bring.php @@ -41,18 +41,21 @@ class Bring protected $uuid; /** - * @var null|int + * @var null|string */ protected $fee; /** - * @var int + * @var string */ protected $discount; - public function __construct(UuidInterface $uuidService) + private MathInterface $math; + + public function __construct(UuidInterface $uuidService, MathInterface $math) { $this->uuid = $uuidService->uuid4(); + $this->math = $math; } public function getStatus(): string @@ -75,7 +78,7 @@ public function setStatus(string $status): self */ public function setDiscount(int $discount): self { - $this->discount = app(MathInterface::class)->round($discount); + $this->discount = $this->math->round($discount); return $this; } @@ -142,22 +145,22 @@ public function setWithdraw(Transaction $withdraw): self public function getDiscount(): int { - return $this->discount; + return (int) $this->discount; } public function getFee(): int { $fee = $this->fee; if ($fee === null) { - $fee = app(MathInterface::class)->round( - app(MathInterface::class)->sub( - app(MathInterface::class)->abs($this->getWithdraw()->amount), - app(MathInterface::class)->abs($this->getDeposit()->amount) + $fee = $this->math->round( + $this->math->sub( + $this->math->abs($this->getWithdraw()->amount), + $this->math->abs($this->getDeposit()->amount) ) ); } - return $fee; + return (int) $fee; } /** @@ -167,7 +170,7 @@ public function getFee(): int */ public function setFee($fee): self { - $this->fee = app(MathInterface::class)->round($fee); + $this->fee = $this->math->round($fee); return $this; } From b25d5572f805761a5d2c64d205b3c44dcc75c373 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 4 Nov 2021 09:00:47 +0300 Subject: [PATCH 18/51] psalm --- src/Objects/Operation.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Objects/Operation.php b/src/Objects/Operation.php index 32a6aad3d..bef0ee1e7 100644 --- a/src/Objects/Operation.php +++ b/src/Objects/Operation.php @@ -23,7 +23,7 @@ class Operation protected $uuid; /** - * @var int + * @var string */ protected $amount; @@ -44,13 +44,19 @@ class Operation private CastService $castService; + private MathInterface $math; + /** * Transaction constructor. */ - public function __construct(UuidInterface $uuidService, CastService $castService) - { + public function __construct( + UuidInterface $uuidService, + CastService $castService, + MathInterface $math + ) { $this->uuid = $uuidService->uuid4(); $this->castService = $castService; + $this->math = $math; } public function getType(): string @@ -64,7 +70,7 @@ public function getUuid(): string } /** - * @return float|int + * @return string */ public function getAmount() { @@ -98,7 +104,7 @@ public function setType(string $type): self */ public function setAmount($amount): self { - $this->amount = app(MathInterface::class)->round($amount); + $this->amount = $this->math->round($amount); return $this; } From 9657a7c2d8f35104374339b2297932a3d7c89f4d Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 4 Nov 2021 10:02:53 +0300 Subject: [PATCH 19/51] add transaction dto --- .../Assembler/TransactionDtoAssembler.php | 39 +++++++ src/Internal/Dto/TransactionDto.php | 101 ++++++++++++++++++ src/Internal/Query/TransactionQuery.php | 24 +++++ .../Repository/TransactionRepository.php | 51 +++++++++ .../Repository/TransferRepository.php | 12 +-- .../Transform/TransactionDtoTransformer.php | 26 +++++ src/Services/CommonService.php | 38 ++++--- tests/Objects/Operation.php | 13 --- tests/Objects/TransactionDtoTransformer.php | 20 ++++ tests/WalletExtensionTest.php | 6 +- 10 files changed, 294 insertions(+), 36 deletions(-) create mode 100644 src/Internal/Assembler/TransactionDtoAssembler.php create mode 100644 src/Internal/Dto/TransactionDto.php create mode 100644 src/Internal/Query/TransactionQuery.php create mode 100644 src/Internal/Repository/TransactionRepository.php create mode 100644 src/Internal/Transform/TransactionDtoTransformer.php delete mode 100644 tests/Objects/Operation.php create mode 100644 tests/Objects/TransactionDtoTransformer.php diff --git a/src/Internal/Assembler/TransactionDtoAssembler.php b/src/Internal/Assembler/TransactionDtoAssembler.php new file mode 100644 index 000000000..1e0045157 --- /dev/null +++ b/src/Internal/Assembler/TransactionDtoAssembler.php @@ -0,0 +1,39 @@ +uuidService = $uuidService; + } + + public function create( + Model $payable, + int $walletId, + string $type, + string $amount, + bool $confirmed, + ?array $meta + ): TransactionDto { + return new TransactionDto( + $this->uuidService->uuid4(), + $payable->getMorphClass(), + $payable->getKey(), + $walletId, + $type, + $amount, + $confirmed, + $meta + ); + } +} diff --git a/src/Internal/Dto/TransactionDto.php b/src/Internal/Dto/TransactionDto.php new file mode 100644 index 000000000..586b7c8d0 --- /dev/null +++ b/src/Internal/Dto/TransactionDto.php @@ -0,0 +1,101 @@ +uuid = $uuid; + $this->payableType = $payableType; + $this->payableId = $payableId; + $this->walletId = $walletId; + $this->type = $type; + $this->amount = $amount; + $this->confirmed = $confirmed; + $this->meta = $meta; + $this->createdAt = new DateTimeImmutable(); + $this->updatedAt = new DateTimeImmutable(); + } + + public function getUuid(): string + { + return $this->uuid; + } + + public function getPayableType(): string + { + return $this->payableType; + } + + public function getPayableId(): int + { + return $this->payableId; + } + + public function getWalletId(): int + { + return $this->walletId; + } + + public function getType(): string + { + return $this->type; + } + + public function getAmount(): string + { + return $this->amount; + } + + public function isConfirmed(): bool + { + return $this->confirmed; + } + + public function getMeta(): ?array + { + return $this->meta; + } + + public function getCreatedAt(): DateTimeImmutable + { + return $this->createdAt; + } + + public function getUpdatedAt(): DateTimeImmutable + { + return $this->updatedAt; + } +} diff --git a/src/Internal/Query/TransactionQuery.php b/src/Internal/Query/TransactionQuery.php new file mode 100644 index 000000000..7818b40a8 --- /dev/null +++ b/src/Internal/Query/TransactionQuery.php @@ -0,0 +1,24 @@ + */ + private array $uuids; + + /** @param non-empty-array $uuids */ + public function __construct(array $uuids) + { + $this->uuids = $uuids; + } + + /** @return non-empty-array */ + public function getUuids(): array + { + return $this->uuids; + } +} diff --git a/src/Internal/Repository/TransactionRepository.php b/src/Internal/Repository/TransactionRepository.php new file mode 100644 index 000000000..4e397565e --- /dev/null +++ b/src/Internal/Repository/TransactionRepository.php @@ -0,0 +1,51 @@ +transformer = $transformer; + $this->transaction = $transaction; + } + + /** + * @param non-empty-array $objects + * + * @return non-empty-array + */ + public function insert(array $objects): array + { + $values = array_map(fn (TransactionDto $dto): array => $this->transformer->extract($dto), $objects); + $this->transaction->newQuery()->insert($values); + + $uuids = array_map(static fn (TransactionDto $dto): string => $dto->getUuid(), $objects); + $query = new TransactionQuery($uuids); + + return $this->findBy($query); + } + + /** @return Transaction[] */ + public function findBy(TransactionQuery $query): array + { + return $this->transaction->newQuery() + ->where('uuid', $query->getUuids()) + ->get() + ->all() + ; + } +} diff --git a/src/Internal/Repository/TransferRepository.php b/src/Internal/Repository/TransferRepository.php index 3e3d27366..1a0147038 100644 --- a/src/Internal/Repository/TransferRepository.php +++ b/src/Internal/Repository/TransferRepository.php @@ -24,26 +24,26 @@ public function __construct( } /** - * @param non-empty-array $transfers + * @param non-empty-array $objects * * @return non-empty-array */ - public function insert(array $transfers): array + public function insert(array $objects): array { - $values = array_map(fn (TransferDto $dto): array => $this->transformer->extract($dto), $transfers); + $values = array_map(fn (TransferDto $dto): array => $this->transformer->extract($dto), $objects); $this->transfer->newQuery()->insert($values); - $uuids = array_map(static fn (TransferDto $dto): string => $dto->getUuid(), $transfers); + $uuids = array_map(static fn (TransferDto $dto): string => $dto->getUuid(), $objects); $query = new TransferQuery($uuids); return $this->findBy($query); } /** @return Transfer[] */ - public function findBy(TransferQuery $transferQuery): array + public function findBy(TransferQuery $query): array { return $this->transfer->newQuery() - ->where('uuid', $transferQuery->getUuids()) + ->where('uuid', $query->getUuids()) ->get() ->all() ; diff --git a/src/Internal/Transform/TransactionDtoTransformer.php b/src/Internal/Transform/TransactionDtoTransformer.php new file mode 100644 index 000000000..d474aaadf --- /dev/null +++ b/src/Internal/Transform/TransactionDtoTransformer.php @@ -0,0 +1,26 @@ + $dto->getUuid(), + 'payable_type' => $dto->getPayableType(), + 'payable_id' => $dto->getPayableId(), + 'wallet_id' => $dto->getWalletId(), + 'type' => $dto->getType(), + 'amount' => $dto->getAmount(), + 'confirmed' => $dto->isConfirmed(), + 'meta' => json_encode($dto->getMeta(), JSON_THROW_ON_ERROR), // @hack + 'created_at' => $dto->getCreatedAt(), + 'updated_at' => $dto->getUpdatedAt(), + ]; + } +} diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 70609c745..90bc96ac4 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -7,11 +7,13 @@ use Bavix\Wallet\Exceptions\BalanceIsEmpty; use Bavix\Wallet\Exceptions\InsufficientFunds; use Bavix\Wallet\Interfaces\Wallet; +use Bavix\Wallet\Internal\Assembler\TransactionDtoAssembler; use Bavix\Wallet\Internal\Assembler\TransferDtoAssembler; use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\Dto\TransferDto; use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Internal\Repository\TransactionRepository; use Bavix\Wallet\Internal\Repository\TransferRepository; use Bavix\Wallet\Internal\Service\CastService; use Bavix\Wallet\Models\Transaction; @@ -33,6 +35,9 @@ class CommonService private BookkeeperInterface $bookkeeper; private ConsistencyInterface $consistency; private TransferDtoAssembler $transferDtoAssembler; + private TransferRepository $transferRepository; + private TransactionDtoAssembler $transactionDtoAssembler; + private TransactionRepository $transactionRepository; public function __construct( DbService $dbService, @@ -42,7 +47,10 @@ public function __construct( WalletService $walletService, BookkeeperInterface $bookkeeper, ConsistencyInterface $consistency, - TransferDtoAssembler $transferDtoAssembler + TransferDtoAssembler $transferDtoAssembler, + TransferRepository $transferRepository, + TransactionDtoAssembler $transactionDtoAssembler, + TransactionRepository $transactionRepository ) { $this->dbService = $dbService; $this->lockService = $lockService; @@ -52,6 +60,9 @@ public function __construct( $this->bookkeeper = $bookkeeper; $this->consistency = $consistency; $this->transferDtoAssembler = $transferDtoAssembler; + $this->transferRepository = $transferRepository; + $this->transactionDtoAssembler = $transactionDtoAssembler; + $this->transactionRepository = $transactionRepository; } /** @@ -158,7 +169,7 @@ public function deposit(Wallet $wallet, $amount, ?array $meta, bool $confirmed = /** * Create Operation without DB::transaction. * - * @param Operation[] $operations + * @param non-empty-array $operations * * @deprecated */ @@ -172,21 +183,20 @@ public function multiOperation(Wallet $self, array $operations): array $amount = $this->math->add($amount, $operation->getAmount()); } - $objects[$operation->getUuid()] = $operation - ->setWallet($self) - ->toArray() - ; + $objects[$operation->getUuid()] = $this->transactionDtoAssembler->create( + $this->castService->getHolder($self), + $this->castService->getWallet($self)->getKey(), + $operation->getType(), + $operation->getAmount(), + $operation->isConfirmed(), + $operation->getMeta() + ); } - $model = app(config('wallet.transaction.model', Transaction::class)); - $model->insert(array_values($objects)); + $results = $this->transactionRepository->insert($objects); $this->addBalance($self, $amount); - return $model->query() - ->where('uuid', array_keys($objects)) - ->get() - ->all() - ; + return $results; }); } @@ -231,7 +241,7 @@ public function multiBrings(array $brings): array $brings ); - return app(TransferRepository::class)->insert($objects); + return $this->transferRepository->insert($objects); }); } diff --git a/tests/Objects/Operation.php b/tests/Objects/Operation.php deleted file mode 100644 index 04faf6d7a..000000000 --- a/tests/Objects/Operation.php +++ /dev/null @@ -1,13 +0,0 @@ - $this->meta['bank_method'] ?? null, - ]); - } -} diff --git a/tests/Objects/TransactionDtoTransformer.php b/tests/Objects/TransactionDtoTransformer.php new file mode 100644 index 000000000..d00452b27 --- /dev/null +++ b/tests/Objects/TransactionDtoTransformer.php @@ -0,0 +1,20 @@ +getMeta() !== null) { + $bankMethod = $dto->getMeta()['bank_method'] ?? null; + } + + return array_merge(parent::extract($dto), [ + 'bank_method' => $bankMethod, + ]); + } +} diff --git a/tests/WalletExtensionTest.php b/tests/WalletExtensionTest.php index 16c0451ae..3122045e5 100644 --- a/tests/WalletExtensionTest.php +++ b/tests/WalletExtensionTest.php @@ -2,7 +2,7 @@ namespace Bavix\Wallet\Test; -use Bavix\Wallet\Objects\Operation; +use Bavix\Wallet\Internal\Transform\TransactionDtoTransformer; use Bavix\Wallet\Test\Common\Models\Transaction; use Bavix\Wallet\Test\Common\Models\TransactionMoney; use Bavix\Wallet\Test\Factories\BuyerFactory; @@ -16,7 +16,7 @@ class WalletExtensionTest extends TestCase public function setUp(): void { parent::setUp(); - $this->app->bind(Operation::class, Objects\Operation::class); + $this->app->bind(TransactionDtoTransformer::class, Objects\TransactionDtoTransformer::class); } public function testCustomAttribute(): void @@ -33,7 +33,7 @@ public function testCustomAttribute(): void public function testTransactionMoneyAttribute(): void { - $this->app['config']->set('wallet.transaction.model', TransactionMoney::class); + $this->app->bind(\Bavix\Wallet\Models\Transaction::class, TransactionMoney::class); /** * @var Buyer $buyer From 9873efc17479d79352ded56bace7fedb3c0947fb Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 4 Nov 2021 10:07:31 +0300 Subject: [PATCH 20/51] coverage --- src/Objects/Operation.php | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/src/Objects/Operation.php b/src/Objects/Operation.php index bef0ee1e7..8fac3a5bc 100644 --- a/src/Objects/Operation.php +++ b/src/Objects/Operation.php @@ -7,7 +7,6 @@ use Bavix\Wallet\Internal\Service\CastService; use Bavix\Wallet\Internal\UuidInterface; use Bavix\Wallet\Models\Transaction; -use DateTimeImmutable; /** @deprecated There is no alternative yet, but the class will be removed */ class Operation @@ -128,39 +127,4 @@ public function setConfirmed(bool $confirmed): self return $this; } - - public function getWallet(): Wallet - { - return $this->wallet; - } - - /** - * @return static - */ - public function setWallet(Wallet $wallet): self - { - $this->wallet = $wallet; - - return $this; - } - - public function toArray(): array - { - $wallet = $this->castService->getWallet($this->getWallet()); - $payable = $this->castService->getHolder($wallet); - $meta = $this->getMeta(); - - return [ - 'type' => $this->getType(), - 'payable_type' => $payable->getMorphClass(), - 'payable_id' => $payable->getKey(), - 'wallet_id' => $wallet->getKey(), - 'uuid' => $this->getUuid(), - 'confirmed' => $this->isConfirmed(), - 'amount' => $this->getAmount(), - 'meta' => $meta === null ? null : json_encode($meta, JSON_THROW_ON_ERROR), - 'created_at' => new DateTimeImmutable(), - 'updated_at' => new DateTimeImmutable(), - ]; - } } From 0ddb8b5665e9defb0c965a45897b52cdc5133d4f Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 4 Nov 2021 11:14:40 +0300 Subject: [PATCH 21/51] update contracts --- src/Interfaces/Discount.php | 2 +- src/Interfaces/Wallet.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Interfaces/Discount.php b/src/Interfaces/Discount.php index e91053442..3c06dd597 100644 --- a/src/Interfaces/Discount.php +++ b/src/Interfaces/Discount.php @@ -4,7 +4,7 @@ namespace Bavix\Wallet\Interfaces; -interface Discount extends Product +interface Discount { /** * @return float|int diff --git a/src/Interfaces/Wallet.php b/src/Interfaces/Wallet.php index 334af6252..6b8d49900 100644 --- a/src/Interfaces/Wallet.php +++ b/src/Interfaces/Wallet.php @@ -67,6 +67,8 @@ public function canWithdraw($amount, bool $allowZero = false): bool; */ public function getBalanceAttribute(); + public function getBalanceIntAttribute(): int; + public function transactions(): MorphMany; public function transfers(): MorphMany; From 735f93efa7617d1c06351eb4993738a8aa51c28c Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 4 Nov 2021 11:28:10 +0300 Subject: [PATCH 22/51] remove ConsistencyInterface from WalletService --- src/Services/WalletService.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Services/WalletService.php b/src/Services/WalletService.php index 5ba3a9c9e..28172c2d0 100644 --- a/src/Services/WalletService.php +++ b/src/Services/WalletService.php @@ -8,7 +8,6 @@ use Bavix\Wallet\Interfaces\Taxable; use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Internal\BookkeeperInterface; -use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Internal\Service\CastService; use Bavix\Wallet\Models\Wallet as WalletModel; @@ -16,7 +15,6 @@ class WalletService { - private ConsistencyInterface $consistency; private DbService $dbService; private MathInterface $math; private LockService $lockService; @@ -26,14 +24,12 @@ public function __construct( DbService $dbService, MathInterface $math, LockService $lockService, - BookkeeperInterface $bookkeeper, - ConsistencyInterface $consistency + BookkeeperInterface $bookkeeper ) { $this->dbService = $dbService; $this->math = $math; $this->lockService = $lockService; $this->bookkeeper = $bookkeeper; - $this->consistency = $consistency; } /** From 0dfb86046d3ceb465b4b63faad2a4bf1706c0436 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 4 Nov 2021 20:14:50 +0300 Subject: [PATCH 23/51] atm state --- .../Repository/TransactionRepository.php | 11 +-- .../Repository/TransferRepository.php | 11 +-- src/Internal/Service/AtmService.php | 75 +++++++++++++++++++ src/Internal/Service/CastService.php | 1 + src/Services/CommonService.php | 36 ++++----- 5 files changed, 99 insertions(+), 35 deletions(-) create mode 100644 src/Internal/Service/AtmService.php diff --git a/src/Internal/Repository/TransactionRepository.php b/src/Internal/Repository/TransactionRepository.php index 4e397565e..8d4541c31 100644 --- a/src/Internal/Repository/TransactionRepository.php +++ b/src/Internal/Repository/TransactionRepository.php @@ -24,19 +24,12 @@ public function __construct( } /** - * @param non-empty-array $objects - * - * @return non-empty-array + * @param non-empty-array $objects */ - public function insert(array $objects): array + public function insert(array $objects): void { $values = array_map(fn (TransactionDto $dto): array => $this->transformer->extract($dto), $objects); $this->transaction->newQuery()->insert($values); - - $uuids = array_map(static fn (TransactionDto $dto): string => $dto->getUuid(), $objects); - $query = new TransactionQuery($uuids); - - return $this->findBy($query); } /** @return Transaction[] */ diff --git a/src/Internal/Repository/TransferRepository.php b/src/Internal/Repository/TransferRepository.php index 1a0147038..2d0789e69 100644 --- a/src/Internal/Repository/TransferRepository.php +++ b/src/Internal/Repository/TransferRepository.php @@ -24,19 +24,12 @@ public function __construct( } /** - * @param non-empty-array $objects - * - * @return non-empty-array + * @param non-empty-array $objects */ - public function insert(array $objects): array + public function insert(array $objects): void { $values = array_map(fn (TransferDto $dto): array => $this->transformer->extract($dto), $objects); $this->transfer->newQuery()->insert($values); - - $uuids = array_map(static fn (TransferDto $dto): string => $dto->getUuid(), $objects); - $query = new TransferQuery($uuids); - - return $this->findBy($query); } /** @return Transfer[] */ diff --git a/src/Internal/Service/AtmService.php b/src/Internal/Service/AtmService.php new file mode 100644 index 000000000..25f1235df --- /dev/null +++ b/src/Internal/Service/AtmService.php @@ -0,0 +1,75 @@ +transactionRepository = $transactionRepository; + $this->transferRepository = $transferRepository; + $this->bookkeeper = $bookkeeper; + } + + /** + * @param non-empty-array $objects + * + * @return non-empty-array + */ + public function makeTransactions(array $objects): array + { + $this->transactionRepository->insert($objects); + $query = new TransactionQuery(array_keys($objects)); + + $items = $this->transactionRepository->findBy($query); + assert(count($items) > 0); + + $results = []; + foreach ($items as $item) { + $results[$item->uuid] = $item; + } + + return $results; + } + + /** + * @param non-empty-array $objects + * + * @return non-empty-array + */ + public function makeTransfers(array $objects): array + { + $this->transferRepository->insert($objects); + $query = new TransferQuery(array_keys($objects)); + + $items = $this->transferRepository->findBy($query); + assert(count($items) > 0); + + $results = []; + foreach ($items as $item) { + $results[$item->uuid] = $item; + } + + return $results; + } +} diff --git a/src/Internal/Service/CastService.php b/src/Internal/Service/CastService.php index d5678dc07..38747dfa5 100644 --- a/src/Internal/Service/CastService.php +++ b/src/Internal/Service/CastService.php @@ -16,6 +16,7 @@ public function getWallet(Wallet $object, bool $save = true): WalletModel $wallet = $this->getModel($object); if (!($wallet instanceof WalletModel)) { $wallet = $wallet->getAttribute('wallet'); + assert($wallet instanceof WalletModel); } if ($save) { diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 90bc96ac4..c2251cefd 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -11,10 +11,8 @@ use Bavix\Wallet\Internal\Assembler\TransferDtoAssembler; use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; -use Bavix\Wallet\Internal\Dto\TransferDto; use Bavix\Wallet\Internal\MathInterface; -use Bavix\Wallet\Internal\Repository\TransactionRepository; -use Bavix\Wallet\Internal\Repository\TransferRepository; +use Bavix\Wallet\Internal\Service\AtmService; use Bavix\Wallet\Internal\Service\CastService; use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; @@ -30,14 +28,13 @@ class CommonService private DbService $dbService; private LockService $lockService; private MathInterface $math; + private AtmService $atmService; private CastService $castService; private WalletService $walletService; private BookkeeperInterface $bookkeeper; private ConsistencyInterface $consistency; private TransferDtoAssembler $transferDtoAssembler; - private TransferRepository $transferRepository; private TransactionDtoAssembler $transactionDtoAssembler; - private TransactionRepository $transactionRepository; public function __construct( DbService $dbService, @@ -48,21 +45,19 @@ public function __construct( BookkeeperInterface $bookkeeper, ConsistencyInterface $consistency, TransferDtoAssembler $transferDtoAssembler, - TransferRepository $transferRepository, TransactionDtoAssembler $transactionDtoAssembler, - TransactionRepository $transactionRepository + AtmService $atmService ) { $this->dbService = $dbService; $this->lockService = $lockService; $this->math = $math; + $this->atmService = $atmService; $this->castService = $castService; $this->walletService = $walletService; $this->bookkeeper = $bookkeeper; $this->consistency = $consistency; $this->transferDtoAssembler = $transferDtoAssembler; - $this->transferRepository = $transferRepository; $this->transactionDtoAssembler = $transactionDtoAssembler; - $this->transactionRepository = $transactionRepository; } /** @@ -172,6 +167,7 @@ public function deposit(Wallet $wallet, $amount, ?array $meta, bool $confirmed = * @param non-empty-array $operations * * @deprecated + * @see AtmService::makeTransactions() */ public function multiOperation(Wallet $self, array $operations): array { @@ -183,7 +179,7 @@ public function multiOperation(Wallet $self, array $operations): array $amount = $this->math->add($amount, $operation->getAmount()); } - $objects[$operation->getUuid()] = $this->transactionDtoAssembler->create( + $object = $this->transactionDtoAssembler->create( $this->castService->getHolder($self), $this->castService->getWallet($self)->getKey(), $operation->getType(), @@ -191,9 +187,11 @@ public function multiOperation(Wallet $self, array $operations): array $operation->isConfirmed(), $operation->getMeta() ); + + $objects[$object->getUuid()] = $object; } - $results = $this->transactionRepository->insert($objects); + $results = $this->atmService->makeTransactions($objects); $this->addBalance($self, $amount); return $results; @@ -206,6 +204,7 @@ public function multiOperation(Wallet $self, array $operations): array * @param Bring[] $brings * * @deprecated + * @see AtmService::makeTransfers() */ public function assemble(array $brings): array { @@ -224,12 +223,14 @@ public function assemble(array $brings): array * @param non-empty-array $brings * * @deprecated + * @see AtmService::makeTransfers() */ public function multiBrings(array $brings): array { return $this->lockService->lock($this, __FUNCTION__, function () use ($brings) { - $objects = array_map( - fn (Bring $bring): TransferDto => $this->transferDtoAssembler->create( + $objects = []; + foreach ($brings as $bring) { + $object = $this->transferDtoAssembler->create( $bring->getDeposit()->getKey(), $bring->getWithdraw()->getKey(), $bring->getStatus(), @@ -237,11 +238,12 @@ public function multiBrings(array $brings): array $this->castService->getModel($bring->getTo()), $bring->getDiscount(), $bring->getFee() - ), - $brings - ); + ); + + $objects[$object->getUuid()] = $object; + } - return $this->transferRepository->insert($objects); + return $this->atmService->makeTransfers($objects); }); } From 9dfc35e4b52358687c8fc6997cd599a008b7b232 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Fri, 5 Nov 2021 23:16:32 +0300 Subject: [PATCH 24/51] drop old code --- src/Objects/Bring.php | 44 ++++++------------------------ src/Objects/Cart.php | 39 ++++++++++---------------- src/Objects/Operation.php | 50 ++++------------------------------ src/Services/WalletService.php | 5 +++- tests/Models/Item.php | 5 ---- 5 files changed, 32 insertions(+), 111 deletions(-) diff --git a/src/Objects/Bring.php b/src/Objects/Bring.php index 00030be2b..2ca8c352e 100644 --- a/src/Objects/Bring.php +++ b/src/Objects/Bring.php @@ -4,57 +4,29 @@ use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Internal\MathInterface; -use Bavix\Wallet\Internal\UuidInterface; use Bavix\Wallet\Models\Transaction; /** @deprecated There is no alternative yet, but the class will be removed */ class Bring { - /** - * @var string - */ - protected $status; + private string $status; - /** - * @var Wallet - */ - protected $from; + private Wallet $from; - /** - * @var Wallet - */ - protected $to; + private Wallet $to; - /** - * @var Transaction - */ - protected $deposit; + private Transaction $deposit; - /** - * @var Transaction - */ - protected $withdraw; - - /** - * @var string - */ - protected $uuid; + private Transaction $withdraw; - /** - * @var null|string - */ - protected $fee; + private ?string $fee = null; - /** - * @var string - */ - protected $discount; + private string $discount; private MathInterface $math; - public function __construct(UuidInterface $uuidService, MathInterface $math) + public function __construct(MathInterface $math) { - $this->uuid = $uuidService->uuid4(); $this->math = $math; } diff --git a/src/Objects/Cart.php b/src/Objects/Cart.php index 8ec21c4d3..bea1e68a0 100644 --- a/src/Objects/Cart.php +++ b/src/Objects/Cart.php @@ -11,10 +11,10 @@ use Bavix\Wallet\Internal\Dto\BasketDto; use Bavix\Wallet\Internal\Dto\ItemDto; use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Internal\Service\CastService; use function count; use Countable; use function get_class; -use Illuminate\Database\Eloquent\Model; class Cart implements Countable, CartInterface { @@ -28,11 +28,15 @@ class Cart implements Countable, CartInterface private array $meta = []; + private CastService $castService; + private MathInterface $math; public function __construct( + CastService $castService, MathInterface $math ) { + $this->castService = $castService; $this->math = $math; } @@ -48,22 +52,15 @@ public function setMeta(array $meta): self return $this; } - /** - * @return static - */ public function addItem(Product $product, int $quantity = 1): self { $this->addQuantity($product, $quantity); - for ($i = 0; $i < $quantity; ++$i) { - $this->items[] = $product; - } + $products = array_fill(0, $quantity, $product); + $this->items = array_merge($this->items, $products); return $this; } - /** - * @return static - */ public function addItems(iterable $products): self { foreach ($products as $product) { @@ -106,32 +103,26 @@ public function count(): int public function getQuantity(Product $product): int { - /** @var Model $product */ - $uniq = (string) (method_exists($product, 'getUniqueId') - ? $product->getUniqueId() - : $product->getKey()); + $model = $this->castService->getModel($product); - return (int) ($this->quantity[get_class($product).':'.$uniq] ?? 0); + return (int) ($this->quantity[get_class($product).':'.$model->getKey()] ?? 0); } public function getBasketDto(): BasketDto { - $items = []; - foreach ($this->getUniqueItems() as $product) { - $items[] = new ItemDto($product, $this->getQuantity($product)); - } + $items = array_map( + fn (Product $product): ItemDto => new ItemDto($product, $this->getQuantity($product)), + $this->getUniqueItems() + ); return new BasketDto($items, $this->getMeta()); } protected function addQuantity(Product $product, int $quantity): void { - /** @var Model|Product $product */ - $uniq = (string) (method_exists($product, 'getUniqueId') - ? $product->getUniqueId() - : $product->getKey()); + $model = $this->castService->getModel($product); - $this->quantity[get_class($product).':'.$uniq] = $this->math + $this->quantity[get_class($product).':'.$model->getKey()] = $this->math ->add($this->getQuantity($product), $quantity) ; } diff --git a/src/Objects/Operation.php b/src/Objects/Operation.php index 8fac3a5bc..4d47b9878 100644 --- a/src/Objects/Operation.php +++ b/src/Objects/Operation.php @@ -2,46 +2,18 @@ namespace Bavix\Wallet\Objects; -use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Internal\MathInterface; -use Bavix\Wallet\Internal\Service\CastService; -use Bavix\Wallet\Internal\UuidInterface; -use Bavix\Wallet\Models\Transaction; /** @deprecated There is no alternative yet, but the class will be removed */ class Operation { - /** - * @var string - */ - protected $type; - - /** - * @var string - */ - protected $uuid; - - /** - * @var string - */ - protected $amount; - - /** - * @var null|array - */ - protected $meta; + private string $type; - /** - * @var bool - */ - protected $confirmed; + private string $amount; - /** - * @var Wallet - */ - protected $wallet; + private ?array $meta; - private CastService $castService; + private bool $confirmed; private MathInterface $math; @@ -49,12 +21,8 @@ class Operation * Transaction constructor. */ public function __construct( - UuidInterface $uuidService, - CastService $castService, MathInterface $math ) { - $this->uuid = $uuidService->uuid4(); - $this->castService = $castService; $this->math = $math; } @@ -63,15 +31,7 @@ public function getType(): string return $this->type; } - public function getUuid(): string - { - return $this->uuid; - } - - /** - * @return string - */ - public function getAmount() + public function getAmount(): string { return $this->amount; } diff --git a/src/Services/WalletService.php b/src/Services/WalletService.php index 28172c2d0..9e335e95f 100644 --- a/src/Services/WalletService.php +++ b/src/Services/WalletService.php @@ -17,17 +17,20 @@ class WalletService { private DbService $dbService; private MathInterface $math; + private CastService $castService; private LockService $lockService; private BookkeeperInterface $bookkeeper; public function __construct( DbService $dbService, MathInterface $math, + CastService $castService, LockService $lockService, BookkeeperInterface $bookkeeper ) { $this->dbService = $dbService; $this->math = $math; + $this->castService = $castService; $this->lockService = $lockService; $this->bookkeeper = $bookkeeper; } @@ -82,7 +85,7 @@ public function fee(Wallet $wallet, $amount) public function getWallet(Wallet $object, bool $autoSave = true): WalletModel { - return app(CastService::class)->getWallet($object, $autoSave); + return $this->castService->getWallet($object, $autoSave); } /** diff --git a/tests/Models/Item.php b/tests/Models/Item.php index 5bf8b6c9c..26354e601 100644 --- a/tests/Models/Item.php +++ b/tests/Models/Item.php @@ -54,11 +54,6 @@ public function getMetaProduct(): ?array return null; } - public function getUniqueId(): string - { - return $this->getKey(); - } - /** * @param int[] $walletIds */ From 7f5daa140d4bf797144c9ed07dfad4f03f0b0381 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Fri, 5 Nov 2021 23:41:57 +0300 Subject: [PATCH 25/51] fix filter bug --- src/Internal/Service/JsonService.php | 16 ++++++++++++++++ .../Transform/TransactionDtoTransformer.php | 10 +++++++++- tests/FilterTest.php | 2 ++ 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 src/Internal/Service/JsonService.php diff --git a/src/Internal/Service/JsonService.php b/src/Internal/Service/JsonService.php new file mode 100644 index 000000000..fe7920bf0 --- /dev/null +++ b/src/Internal/Service/JsonService.php @@ -0,0 +1,16 @@ +jsonService = $jsonService; + } + public function extract(TransactionDto $dto): array { return [ @@ -18,7 +26,7 @@ public function extract(TransactionDto $dto): array 'type' => $dto->getType(), 'amount' => $dto->getAmount(), 'confirmed' => $dto->isConfirmed(), - 'meta' => json_encode($dto->getMeta(), JSON_THROW_ON_ERROR), // @hack + 'meta' => $this->jsonService->encode($dto->getMeta()), 'created_at' => $dto->getCreatedAt(), 'updated_at' => $dto->getUpdatedAt(), ]; diff --git a/tests/FilterTest.php b/tests/FilterTest.php index e82004639..599b7fdd6 100644 --- a/tests/FilterTest.php +++ b/tests/FilterTest.php @@ -23,10 +23,12 @@ public function testMetaAccount(): void self::assertEquals(4, $buyer->transactions()->count()); + $nullable = $buyer->transactions()->whereNull('meta')->count(); $customers = $buyer->transactions()->where('meta->account', 'customers')->count(); $expenses = $buyer->transactions()->where('meta->account', 'expenses')->count(); $vendors = $buyer->transactions()->where('meta->account', 'vendors')->count(); + self::assertEquals(1, $nullable); self::assertEquals(1, $customers); self::assertEquals(1, $expenses); self::assertEquals(1, $vendors); From 165e939797a8e6360b89aa871239c544ff610650 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sun, 7 Nov 2021 23:06:01 +0300 Subject: [PATCH 26/51] drop Operation::class --- .phpstorm.meta.php | 2 - src/Internal/Service/AssistantService.php | 50 +++++++++++ src/Internal/Service/AtmService.php | 17 ++-- src/Internal/Service/PrepareService.php | 64 ++++++++++++++ src/Objects/Operation.php | 90 ------------------- src/Services/CommonService.php | 102 ++++++++-------------- src/WalletServiceProvider.php | 2 - tests/SingletonTest.php | 6 -- tests/WalletFloatTest.php | 4 +- 9 files changed, 163 insertions(+), 174 deletions(-) create mode 100644 src/Internal/Service/AssistantService.php create mode 100644 src/Internal/Service/PrepareService.php delete mode 100644 src/Objects/Operation.php diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index b64e31712..e62a0a84a 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -18,8 +18,6 @@ override(\app(0), map([ Cart::class => Cart::class, Bring::class => Bring::class, - Operation::class => Operation::class, - EmptyLock::class => EmptyLock::class, ExchangeService::class => ExchangeService::class, CommonService::class => CommonService::class, WalletService::class => WalletService::class, diff --git a/src/Internal/Service/AssistantService.php b/src/Internal/Service/AssistantService.php new file mode 100644 index 000000000..9234a9215 --- /dev/null +++ b/src/Internal/Service/AssistantService.php @@ -0,0 +1,50 @@ +mathService = $mathService; + } + + /** + * @param TransactionDto[]|TransferDto[] $objects + * + * @return string[] + */ + public function getUuids(array $objects): array + { + return array_map(static fn ($object): string => $object->getUuid(), $objects); + } + + /** + * @param Transaction[] $transactions + * + * @return string[] + */ + public function getSums(array $transactions): array + { + $amounts = []; + foreach ($transactions as $transaction) { + if ($transaction->confirmed) { + $amounts[$transaction->wallet_id] = $this->mathService->add( + $amounts[$transaction->wallet_id] ?? 0, + $transaction->amount + ); + } + } + + return $amounts; + } +} diff --git a/src/Internal/Service/AtmService.php b/src/Internal/Service/AtmService.php index 25f1235df..8987eca2a 100644 --- a/src/Internal/Service/AtmService.php +++ b/src/Internal/Service/AtmService.php @@ -19,30 +19,34 @@ class AtmService { private TransactionRepository $transactionRepository; private TransferRepository $transferRepository; + private AssistantService $assistantService; private BookkeeperInterface $bookkeeper; public function __construct( TransactionRepository $transactionRepository, TransferRepository $transferRepository, + AssistantService $assistantService, BookkeeperInterface $bookkeeper ) { $this->transactionRepository = $transactionRepository; $this->transferRepository = $transferRepository; + $this->assistantService = $assistantService; $this->bookkeeper = $bookkeeper; } /** - * @param non-empty-array $objects + * @param non-empty-array $objects * * @return non-empty-array */ public function makeTransactions(array $objects): array { $this->transactionRepository->insert($objects); - $query = new TransactionQuery(array_keys($objects)); + $uuids = $this->assistantService->getUuids($objects); + $query = new TransactionQuery($uuids); $items = $this->transactionRepository->findBy($query); - assert(count($items) > 0); + assert(count($items) === count($uuids)); $results = []; foreach ($items as $item) { @@ -53,17 +57,18 @@ public function makeTransactions(array $objects): array } /** - * @param non-empty-array $objects + * @param non-empty-array $objects * * @return non-empty-array */ public function makeTransfers(array $objects): array { $this->transferRepository->insert($objects); - $query = new TransferQuery(array_keys($objects)); + $uuids = $this->assistantService->getUuids($objects); + $query = new TransferQuery($uuids); $items = $this->transferRepository->findBy($query); - assert(count($items) > 0); + assert(count($items) === count($uuids)); $results = []; foreach ($items as $item) { diff --git a/src/Internal/Service/PrepareService.php b/src/Internal/Service/PrepareService.php new file mode 100644 index 000000000..292e1ae18 --- /dev/null +++ b/src/Internal/Service/PrepareService.php @@ -0,0 +1,64 @@ +transactionDtoAssembler = $transactionDtoAssembler; + $this->transferDtoAssembler = $transferDtoAssembler; + $this->consistencyService = $consistencyService; + $this->castService = $castService; + $this->mathService = $mathService; + } + + public function deposit(Wallet $wallet, string $amount, ?array $meta, bool $confirmed = true): TransactionDto + { + $this->consistencyService->checkPositive($amount); + + return $this->transactionDtoAssembler->create( + $this->castService->getHolder($wallet), + $this->castService->getWallet($wallet)->getKey(), + Transaction::TYPE_DEPOSIT, + $amount, + $confirmed, + $meta + ); + } + + public function withdraw(Wallet $wallet, string $amount, ?array $meta, bool $confirmed = true): TransactionDto + { + $this->consistencyService->checkPositive($amount); + + return $this->transactionDtoAssembler->create( + $this->castService->getHolder($wallet), + $this->castService->getWallet($wallet)->getKey(), + Transaction::TYPE_WITHDRAW, + $this->mathService->negative($amount), + $confirmed, + $meta + ); + } +} diff --git a/src/Objects/Operation.php b/src/Objects/Operation.php deleted file mode 100644 index 4d47b9878..000000000 --- a/src/Objects/Operation.php +++ /dev/null @@ -1,90 +0,0 @@ -math = $math; - } - - public function getType(): string - { - return $this->type; - } - - public function getAmount(): string - { - return $this->amount; - } - - public function getMeta(): ?array - { - return $this->meta; - } - - public function isConfirmed(): bool - { - return $this->confirmed; - } - - /** - * @return static - */ - public function setType(string $type): self - { - $this->type = $type; - - return $this; - } - - /** - * @param int|string $amount - * - * @return static - */ - public function setAmount($amount): self - { - $this->amount = $this->math->round($amount); - - return $this; - } - - /** - * @return static - */ - public function setMeta(?array $meta): self - { - $this->meta = $meta; - - return $this; - } - - /** - * @return static - */ - public function setConfirmed(bool $confirmed): self - { - $this->confirmed = $confirmed; - - return $this; - } -} diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index c2251cefd..e3db6ce59 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -12,13 +12,14 @@ use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Internal\Service\AssistantService; use Bavix\Wallet\Internal\Service\AtmService; use Bavix\Wallet\Internal\Service\CastService; +use Bavix\Wallet\Internal\Service\PrepareService; use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Models\Wallet as WalletModel; use Bavix\Wallet\Objects\Bring; -use Bavix\Wallet\Objects\Operation; use function compact; use function max; use Throwable; @@ -31,6 +32,8 @@ class CommonService private AtmService $atmService; private CastService $castService; private WalletService $walletService; + private AssistantService $assistantService; + private PrepareService $prepareService; private BookkeeperInterface $bookkeeper; private ConsistencyInterface $consistency; private TransferDtoAssembler $transferDtoAssembler; @@ -44,6 +47,8 @@ public function __construct( WalletService $walletService, BookkeeperInterface $bookkeeper, ConsistencyInterface $consistency, + AssistantService $satisfyService, + PrepareService $prepareService, TransferDtoAssembler $transferDtoAssembler, TransactionDtoAssembler $transactionDtoAssembler, AtmService $atmService @@ -56,6 +61,8 @@ public function __construct( $this->walletService = $walletService; $this->bookkeeper = $bookkeeper; $this->consistency = $consistency; + $this->assistantService = $satisfyService; + $this->prepareService = $prepareService; $this->transferDtoAssembler = $transferDtoAssembler; $this->transactionDtoAssembler = $transactionDtoAssembler; } @@ -119,20 +126,7 @@ public function forceTransfer(Wallet $from, Wallet $to, $amount, ?array $meta = public function forceWithdraw(Wallet $wallet, $amount, ?array $meta, bool $confirmed = true): Transaction { return $this->lockService->lock($this, __FUNCTION__, function () use ($wallet, $amount, $meta, $confirmed) { - $this->consistency->checkPositive($amount); - - /** @var WalletModel $wallet */ - $wallet = $this->walletService->getWallet($wallet); - - $transactions = $this->multiOperation($wallet, [ - app(Operation::class) - ->setType(Transaction::TYPE_WITHDRAW) - ->setConfirmed($confirmed) - ->setAmount($this->math->negative($amount)) - ->setMeta($meta), - ]); - - return current($transactions); + return $this->operation($wallet, Transaction::TYPE_WITHDRAW, $amount, $meta, $confirmed); }); } @@ -144,57 +138,7 @@ public function forceWithdraw(Wallet $wallet, $amount, ?array $meta, bool $confi public function deposit(Wallet $wallet, $amount, ?array $meta, bool $confirmed = true): Transaction { return $this->lockService->lock($this, __FUNCTION__, function () use ($wallet, $amount, $meta, $confirmed) { - $this->consistency->checkPositive($amount); - - /** @var WalletModel $wallet */ - $wallet = $this->walletService->getWallet($wallet); - - $transactions = $this->multiOperation($wallet, [ - app(Operation::class) - ->setType(Transaction::TYPE_DEPOSIT) - ->setConfirmed($confirmed) - ->setAmount($amount) - ->setMeta($meta), - ]); - - return current($transactions); - }); - } - - /** - * Create Operation without DB::transaction. - * - * @param non-empty-array $operations - * - * @deprecated - * @see AtmService::makeTransactions() - */ - public function multiOperation(Wallet $self, array $operations): array - { - return $this->lockService->lock($this, __FUNCTION__, function () use ($self, $operations) { - $amount = 0; - $objects = []; - foreach ($operations as $operation) { - if ($operation->isConfirmed()) { - $amount = $this->math->add($amount, $operation->getAmount()); - } - - $object = $this->transactionDtoAssembler->create( - $this->castService->getHolder($self), - $this->castService->getWallet($self)->getKey(), - $operation->getType(), - $operation->getAmount(), - $operation->isConfirmed(), - $operation->getMeta() - ); - - $objects[$object->getUuid()] = $object; - } - - $results = $this->atmService->makeTransactions($objects); - $this->addBalance($self, $amount); - - return $results; + return $this->operation($wallet, Transaction::TYPE_DEPOSIT, $amount, $meta, $confirmed); }); } @@ -281,4 +225,30 @@ public function addBalance(Wallet $wallet, $amount): bool return $result; }); } + + protected function operation(Wallet $wallet, string $type, $amount, ?array $meta, bool $confirmed = true): Transaction + { + assert(in_array($type, [Transaction::TYPE_DEPOSIT, Transaction::TYPE_WITHDRAW], true)); + + if ($type === Transaction::TYPE_DEPOSIT) { + $dto = $this->prepareService->deposit($wallet, (string) $amount, $meta, $confirmed); + } else { + $dto = $this->prepareService->withdraw($wallet, (string) $amount, $meta, $confirmed); + } + + $transactions = $this->atmService->makeTransactions([$dto]); // q1 + $object = $this->castService->getWallet($wallet); + $totals = $this->assistantService->getSums($transactions); + $total = (string) ($totals[$object->getKey()] ?? 0); + + // optimize queries + if ($this->math->compare($total, 0) !== 0) { + $balance = $this->bookkeeper->increase($object, $total); + + $object->newQuery()->update(compact('balance')); // ?q2 + $object->fill(compact('balance'))->syncOriginalAttribute('balance'); + } + + return current($transactions); + } } diff --git a/src/WalletServiceProvider.php b/src/WalletServiceProvider.php index 33b90885b..e4e88982c 100644 --- a/src/WalletServiceProvider.php +++ b/src/WalletServiceProvider.php @@ -19,7 +19,6 @@ use Bavix\Wallet\Models\Wallet; use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Objects\Cart; -use Bavix\Wallet\Objects\Operation; use Bavix\Wallet\Services\AtomicService; use Bavix\Wallet\Services\BasketService; use Bavix\Wallet\Services\BookkeeperService; @@ -131,6 +130,5 @@ private function bindObjects(): void // object's $this->app->bind(Bring::class, config('wallet.objects.bring', Bring::class)); $this->app->bind(Cart::class, config('wallet.objects.cart', Cart::class)); - $this->app->bind(Operation::class, config('wallet.objects.operation', Operation::class)); } } diff --git a/tests/SingletonTest.php b/tests/SingletonTest.php index b696a845a..c5d848519 100644 --- a/tests/SingletonTest.php +++ b/tests/SingletonTest.php @@ -5,7 +5,6 @@ use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Objects\Cart; -use Bavix\Wallet\Objects\Operation; use Bavix\Wallet\Services\CommonService; use Bavix\Wallet\Services\DbService; use Bavix\Wallet\Services\LockService; @@ -29,11 +28,6 @@ public function testCart(): void self::assertNotEquals($this->getRefId(Cart::class), $this->getRefId(Cart::class)); } - public function testOperation(): void - { - self::assertNotEquals($this->getRefId(Operation::class), $this->getRefId(Operation::class)); - } - public function testMathInterface(): void { self::assertEquals($this->getRefId(MathInterface::class), $this->getRefId(MathInterface::class)); diff --git a/tests/WalletFloatTest.php b/tests/WalletFloatTest.php index 75352a193..24702c46f 100644 --- a/tests/WalletFloatTest.php +++ b/tests/WalletFloatTest.php @@ -261,8 +261,8 @@ public function testEther(): void $math = app(MathInterface::class); $user->depositFloat('545.8754855274419'); - self::assertEquals('545875485527441900000', $user->balance); - self::assertEquals(0, $math->compare($user->balanceFloat, '545.8754855274419')); + self::assertEquals('545875485527442000000', $user->balance); + self::assertEquals(0, $math->compare($user->balanceFloat, '545.8754855274420')); } public function testBitcoin(): void From 9f7c8cd755ce3d10ec7e437f5cd79a97d3d0e21a Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Sun, 7 Nov 2021 23:37:15 +0300 Subject: [PATCH 27/51] fix units --- src/Internal/Service/AssistantService.php | 11 +++++------ src/Services/CommonService.php | 2 +- tests/WalletFloatTest.php | 4 ++-- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Internal/Service/AssistantService.php b/src/Internal/Service/AssistantService.php index 9234a9215..18d1466d1 100644 --- a/src/Internal/Service/AssistantService.php +++ b/src/Internal/Service/AssistantService.php @@ -6,7 +6,6 @@ use Bavix\Wallet\Internal\Dto\TransactionDto; use Bavix\Wallet\Internal\Dto\TransferDto; -use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Services\MathService; class AssistantService @@ -29,7 +28,7 @@ public function getUuids(array $objects): array } /** - * @param Transaction[] $transactions + * @param TransactionDto[] $transactions * * @return string[] */ @@ -37,10 +36,10 @@ public function getSums(array $transactions): array { $amounts = []; foreach ($transactions as $transaction) { - if ($transaction->confirmed) { - $amounts[$transaction->wallet_id] = $this->mathService->add( - $amounts[$transaction->wallet_id] ?? 0, - $transaction->amount + if ($transaction->isConfirmed()) { + $amounts[$transaction->getWalletId()] = $this->mathService->add( + $amounts[$transaction->getWalletId()] ?? 0, + $transaction->getAmount() ); } } diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index e3db6ce59..28899abbe 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -238,7 +238,7 @@ protected function operation(Wallet $wallet, string $type, $amount, ?array $meta $transactions = $this->atmService->makeTransactions([$dto]); // q1 $object = $this->castService->getWallet($wallet); - $totals = $this->assistantService->getSums($transactions); + $totals = $this->assistantService->getSums([$dto]); $total = (string) ($totals[$object->getKey()] ?? 0); // optimize queries diff --git a/tests/WalletFloatTest.php b/tests/WalletFloatTest.php index 24702c46f..75352a193 100644 --- a/tests/WalletFloatTest.php +++ b/tests/WalletFloatTest.php @@ -261,8 +261,8 @@ public function testEther(): void $math = app(MathInterface::class); $user->depositFloat('545.8754855274419'); - self::assertEquals('545875485527442000000', $user->balance); - self::assertEquals(0, $math->compare($user->balanceFloat, '545.8754855274420')); + self::assertEquals('545875485527441900000', $user->balance); + self::assertEquals(0, $math->compare($user->balanceFloat, '545.8754855274419')); } public function testBitcoin(): void From fac092b3092ed9fddafbc1a08d4acc620b519022 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Mon, 8 Nov 2021 18:22:27 +0300 Subject: [PATCH 28/51] drop methods --- src/Services/CommonService.php | 30 +++--------------------------- src/Traits/CanExchange.php | 5 +++-- src/Traits/HasGift.php | 5 +++-- src/Traits/HasWallet.php | 4 ++-- 4 files changed, 11 insertions(+), 33 deletions(-) diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 28899abbe..804dd6c31 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -101,8 +101,8 @@ public function forceTransfer(Wallet $from, Wallet $to, $amount, ?array $meta = $fee = $this->walletService->fee($to, $amount); $amount = max(0, $this->math->sub($amount, $discount)); - $withdraw = $this->forceWithdraw($from, $this->math->add($amount, $fee, $from->decimal_places), $meta); - $deposit = $this->deposit($to, $amount, $meta); + $withdraw = $this->operation($from, Transaction::TYPE_WITHDRAW, $this->math->add($amount, $fee, $from->decimal_places), $meta); + $deposit = $this->operation($to, Transaction::TYPE_DEPOSIT, $amount, $meta); $transfers = $this->multiBrings([ app(Bring::class) @@ -118,30 +118,6 @@ public function forceTransfer(Wallet $from, Wallet $to, $amount, ?array $meta = }); } - /** - * @param int|string $amount - * - * @throws AmountInvalid - */ - public function forceWithdraw(Wallet $wallet, $amount, ?array $meta, bool $confirmed = true): Transaction - { - return $this->lockService->lock($this, __FUNCTION__, function () use ($wallet, $amount, $meta, $confirmed) { - return $this->operation($wallet, Transaction::TYPE_WITHDRAW, $amount, $meta, $confirmed); - }); - } - - /** - * @param int|string $amount - * - * @throws AmountInvalid - */ - public function deposit(Wallet $wallet, $amount, ?array $meta, bool $confirmed = true): Transaction - { - return $this->lockService->lock($this, __FUNCTION__, function () use ($wallet, $amount, $meta, $confirmed) { - return $this->operation($wallet, Transaction::TYPE_DEPOSIT, $amount, $meta, $confirmed); - }); - } - /** * Create Bring with DB::transaction. * @@ -226,7 +202,7 @@ public function addBalance(Wallet $wallet, $amount): bool }); } - protected function operation(Wallet $wallet, string $type, $amount, ?array $meta, bool $confirmed = true): Transaction + public function operation(Wallet $wallet, string $type, $amount, ?array $meta, bool $confirmed = true): Transaction { assert(in_array($type, [Transaction::TYPE_DEPOSIT, Transaction::TYPE_WITHDRAW], true)); diff --git a/src/Traits/CanExchange.php b/src/Traits/CanExchange.php index e58552f50..4f1f223e4 100644 --- a/src/Traits/CanExchange.php +++ b/src/Traits/CanExchange.php @@ -6,6 +6,7 @@ use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\ExchangeInterface; use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Services\CommonService; @@ -59,11 +60,11 @@ public function forceExchange(Wallet $to, $amount, ?array $meta = null): Transfe ); $withdraw = app(CommonService::class) - ->forceWithdraw($from, $math->add($amount, $fee), $meta) + ->operation($from, Transaction::TYPE_WITHDRAW, $math->add($amount, $fee), $meta) ; $deposit = app(CommonService::class) - ->deposit($to, $math->floor($math->mul($amount, $rate, 1)), $meta) + ->operation($to, Transaction::TYPE_DEPOSIT, $math->floor($math->mul($amount, $rate, 1)), $meta) ; $transfers = app(CommonService::class)->multiBrings([ diff --git a/src/Traits/HasGift.php b/src/Traits/HasGift.php index 1400cd47d..ec7a735bf 100644 --- a/src/Traits/HasGift.php +++ b/src/Traits/HasGift.php @@ -11,6 +11,7 @@ use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Services\CommonService; @@ -79,8 +80,8 @@ public function gift(Wallet $to, Product $product, bool $force = false): Transfe app(ConsistencyInterface::class)->checkPotential($santa, $math->add($amount, $fee)); } - $withdraw = $commonService->forceWithdraw($santa, $math->add($amount, $fee), $meta); - $deposit = $commonService->deposit($product, $amount, $meta); + $withdraw = $commonService->operation($santa, Transaction::TYPE_WITHDRAW, $math->add($amount, $fee), $meta); + $deposit = $commonService->operation($product, Transaction::TYPE_DEPOSIT, $amount, $meta); $from = app(WalletService::class) ->getWallet($to) diff --git a/src/Traits/HasWallet.php b/src/Traits/HasWallet.php index d5a901997..1b7108ec4 100644 --- a/src/Traits/HasWallet.php +++ b/src/Traits/HasWallet.php @@ -48,7 +48,7 @@ public function deposit($amount, ?array $meta = null, bool $confirmed = true): T return app(DbService::class)->transaction(static function () use ($self, $amount, $meta, $confirmed) { return app(CommonService::class) - ->deposit($self, $amount, $meta, $confirmed) + ->operation($self, Transaction::TYPE_DEPOSIT, $amount, $meta, $confirmed) ; }); } @@ -189,7 +189,7 @@ public function forceWithdraw($amount, ?array $meta = null, bool $confirmed = tr return app(DbService::class)->transaction(static function () use ($self, $amount, $meta, $confirmed) { return app(CommonService::class) - ->forceWithdraw($self, $amount, $meta, $confirmed) + ->operation($self, Transaction::TYPE_WITHDRAW, $amount, $meta, $confirmed) ; }); } From ab0db7043a7716785b706ddf12e84378b0ce8868 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Mon, 8 Nov 2021 18:49:38 +0300 Subject: [PATCH 29/51] update common service --- src/Internal/Service/AssistantService.php | 9 +++-- src/Services/CommonService.php | 40 ++++++++++++++++------- src/Traits/CanExchange.php | 4 +-- src/Traits/HasGift.php | 4 +-- src/Traits/HasWallet.php | 4 +-- 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/src/Internal/Service/AssistantService.php b/src/Internal/Service/AssistantService.php index 18d1466d1..9e68ecaa2 100644 --- a/src/Internal/Service/AssistantService.php +++ b/src/Internal/Service/AssistantService.php @@ -28,9 +28,9 @@ public function getUuids(array $objects): array } /** - * @param TransactionDto[] $transactions + * @param non-empty-array $transactions * - * @return string[] + * @return array */ public function getSums(array $transactions): array { @@ -44,6 +44,9 @@ public function getSums(array $transactions): array } } - return $amounts; + return array_filter( + $amounts, + fn (string $amount): bool => $this->mathService->compare($amount, 0) !== 0 + ); } } diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 804dd6c31..5f288007a 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -11,6 +11,7 @@ use Bavix\Wallet\Internal\Assembler\TransferDtoAssembler; use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; +use Bavix\Wallet\Internal\Dto\TransactionDto; use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Internal\Service\AssistantService; use Bavix\Wallet\Internal\Service\AtmService; @@ -101,8 +102,8 @@ public function forceTransfer(Wallet $from, Wallet $to, $amount, ?array $meta = $fee = $this->walletService->fee($to, $amount); $amount = max(0, $this->math->sub($amount, $discount)); - $withdraw = $this->operation($from, Transaction::TYPE_WITHDRAW, $this->math->add($amount, $fee, $from->decimal_places), $meta); - $deposit = $this->operation($to, Transaction::TYPE_DEPOSIT, $amount, $meta); + $withdraw = $this->makeOperation($from, Transaction::TYPE_WITHDRAW, $this->math->add($amount, $fee, $from->decimal_places), $meta); + $deposit = $this->makeOperation($to, Transaction::TYPE_DEPOSIT, $amount, $meta); $transfers = $this->multiBrings([ app(Bring::class) @@ -202,7 +203,7 @@ public function addBalance(Wallet $wallet, $amount): bool }); } - public function operation(Wallet $wallet, string $type, $amount, ?array $meta, bool $confirmed = true): Transaction + public function makeOperation(Wallet $wallet, string $type, $amount, ?array $meta, bool $confirmed = true): Transaction { assert(in_array($type, [Transaction::TYPE_DEPOSIT, Transaction::TYPE_WITHDRAW], true)); @@ -212,19 +213,36 @@ public function operation(Wallet $wallet, string $type, $amount, ?array $meta, b $dto = $this->prepareService->withdraw($wallet, (string) $amount, $meta, $confirmed); } - $transactions = $this->atmService->makeTransactions([$dto]); // q1 - $object = $this->castService->getWallet($wallet); - $totals = $this->assistantService->getSums([$dto]); - $total = (string) ($totals[$object->getKey()] ?? 0); + $transactions = $this->applyOperations( + [$dto->getWalletId() => $wallet], + [$dto], + ); - // optimize queries - if ($this->math->compare($total, 0) !== 0) { + return current($transactions); + } + + /** + * @param non-empty-array $wallets + * @param non-empty-array $objects + * + * @return non-empty-array + */ + public function applyOperations(array $wallets, array $objects): array + { + $transactions = $this->atmService->makeTransactions($objects); // q1 + $totals = $this->assistantService->getSums($objects); + + foreach ($totals as $walletId => $total) { + $wallet = $wallets[$walletId] ?? null; + assert($wallet !== null); + + $object = $this->walletService->getWallet($wallet); $balance = $this->bookkeeper->increase($object, $total); - $object->newQuery()->update(compact('balance')); // ?q2 + $object->newQuery()->update(compact('balance')); // ?qN $object->fill(compact('balance'))->syncOriginalAttribute('balance'); } - return current($transactions); + return $transactions; } } diff --git a/src/Traits/CanExchange.php b/src/Traits/CanExchange.php index 4f1f223e4..36fa0b961 100644 --- a/src/Traits/CanExchange.php +++ b/src/Traits/CanExchange.php @@ -60,11 +60,11 @@ public function forceExchange(Wallet $to, $amount, ?array $meta = null): Transfe ); $withdraw = app(CommonService::class) - ->operation($from, Transaction::TYPE_WITHDRAW, $math->add($amount, $fee), $meta) + ->makeOperation($from, Transaction::TYPE_WITHDRAW, $math->add($amount, $fee), $meta) ; $deposit = app(CommonService::class) - ->operation($to, Transaction::TYPE_DEPOSIT, $math->floor($math->mul($amount, $rate, 1)), $meta) + ->makeOperation($to, Transaction::TYPE_DEPOSIT, $math->floor($math->mul($amount, $rate, 1)), $meta) ; $transfers = app(CommonService::class)->multiBrings([ diff --git a/src/Traits/HasGift.php b/src/Traits/HasGift.php index ec7a735bf..ae77ba5a8 100644 --- a/src/Traits/HasGift.php +++ b/src/Traits/HasGift.php @@ -80,8 +80,8 @@ public function gift(Wallet $to, Product $product, bool $force = false): Transfe app(ConsistencyInterface::class)->checkPotential($santa, $math->add($amount, $fee)); } - $withdraw = $commonService->operation($santa, Transaction::TYPE_WITHDRAW, $math->add($amount, $fee), $meta); - $deposit = $commonService->operation($product, Transaction::TYPE_DEPOSIT, $amount, $meta); + $withdraw = $commonService->makeOperation($santa, Transaction::TYPE_WITHDRAW, $math->add($amount, $fee), $meta); + $deposit = $commonService->makeOperation($product, Transaction::TYPE_DEPOSIT, $amount, $meta); $from = app(WalletService::class) ->getWallet($to) diff --git a/src/Traits/HasWallet.php b/src/Traits/HasWallet.php index 1b7108ec4..9d2d131ca 100644 --- a/src/Traits/HasWallet.php +++ b/src/Traits/HasWallet.php @@ -48,7 +48,7 @@ public function deposit($amount, ?array $meta = null, bool $confirmed = true): T return app(DbService::class)->transaction(static function () use ($self, $amount, $meta, $confirmed) { return app(CommonService::class) - ->operation($self, Transaction::TYPE_DEPOSIT, $amount, $meta, $confirmed) + ->makeOperation($self, Transaction::TYPE_DEPOSIT, $amount, $meta, $confirmed) ; }); } @@ -189,7 +189,7 @@ public function forceWithdraw($amount, ?array $meta = null, bool $confirmed = tr return app(DbService::class)->transaction(static function () use ($self, $amount, $meta, $confirmed) { return app(CommonService::class) - ->operation($self, Transaction::TYPE_WITHDRAW, $amount, $meta, $confirmed) + ->makeOperation($self, Transaction::TYPE_WITHDRAW, $amount, $meta, $confirmed) ; }); } From 46391644432cfe2ae36c4909f5307e597dcde740 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Mon, 8 Nov 2021 20:45:31 +0300 Subject: [PATCH 30/51] fix repositories --- .../Repository/TransactionRepository.php | 2 +- .../Repository/TransferRepository.php | 2 +- src/Internal/Service/AssistantService.php | 4 ++-- src/Internal/Service/AtmService.php | 6 +---- src/Services/CommonService.php | 22 ++++++++++++++----- src/Traits/HasWallet.php | 2 +- 6 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/Internal/Repository/TransactionRepository.php b/src/Internal/Repository/TransactionRepository.php index 8d4541c31..fa6919148 100644 --- a/src/Internal/Repository/TransactionRepository.php +++ b/src/Internal/Repository/TransactionRepository.php @@ -36,7 +36,7 @@ public function insert(array $objects): void public function findBy(TransactionQuery $query): array { return $this->transaction->newQuery() - ->where('uuid', $query->getUuids()) + ->whereIn('uuid', $query->getUuids()) ->get() ->all() ; diff --git a/src/Internal/Repository/TransferRepository.php b/src/Internal/Repository/TransferRepository.php index 2d0789e69..e62e885ac 100644 --- a/src/Internal/Repository/TransferRepository.php +++ b/src/Internal/Repository/TransferRepository.php @@ -36,7 +36,7 @@ public function insert(array $objects): void public function findBy(TransferQuery $query): array { return $this->transfer->newQuery() - ->where('uuid', $query->getUuids()) + ->whereIn('uuid', $query->getUuids()) ->get() ->all() ; diff --git a/src/Internal/Service/AssistantService.php b/src/Internal/Service/AssistantService.php index 9e68ecaa2..a77c500b0 100644 --- a/src/Internal/Service/AssistantService.php +++ b/src/Internal/Service/AssistantService.php @@ -18,9 +18,9 @@ public function __construct(MathService $mathService) } /** - * @param TransactionDto[]|TransferDto[] $objects + * @param non-empty-array|non-empty-array $objects * - * @return string[] + * @return non-empty-array */ public function getUuids(array $objects): array { diff --git a/src/Internal/Service/AtmService.php b/src/Internal/Service/AtmService.php index 8987eca2a..a076496d2 100644 --- a/src/Internal/Service/AtmService.php +++ b/src/Internal/Service/AtmService.php @@ -4,7 +4,6 @@ namespace Bavix\Wallet\Internal\Service; -use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\Dto\TransactionDto; use Bavix\Wallet\Internal\Dto\TransferDto; use Bavix\Wallet\Internal\Query\TransactionQuery; @@ -20,18 +19,15 @@ class AtmService private TransactionRepository $transactionRepository; private TransferRepository $transferRepository; private AssistantService $assistantService; - private BookkeeperInterface $bookkeeper; public function __construct( TransactionRepository $transactionRepository, TransferRepository $transferRepository, - AssistantService $assistantService, - BookkeeperInterface $bookkeeper + AssistantService $assistantService ) { $this->transactionRepository = $transactionRepository; $this->transferRepository = $transferRepository; $this->assistantService = $assistantService; - $this->bookkeeper = $bookkeeper; } /** diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 5f288007a..c0413d981 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -7,7 +7,6 @@ use Bavix\Wallet\Exceptions\BalanceIsEmpty; use Bavix\Wallet\Exceptions\InsufficientFunds; use Bavix\Wallet\Interfaces\Wallet; -use Bavix\Wallet\Internal\Assembler\TransactionDtoAssembler; use Bavix\Wallet\Internal\Assembler\TransferDtoAssembler; use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; @@ -38,7 +37,6 @@ class CommonService private BookkeeperInterface $bookkeeper; private ConsistencyInterface $consistency; private TransferDtoAssembler $transferDtoAssembler; - private TransactionDtoAssembler $transactionDtoAssembler; public function __construct( DbService $dbService, @@ -51,7 +49,6 @@ public function __construct( AssistantService $satisfyService, PrepareService $prepareService, TransferDtoAssembler $transferDtoAssembler, - TransactionDtoAssembler $transactionDtoAssembler, AtmService $atmService ) { $this->dbService = $dbService; @@ -65,7 +62,6 @@ public function __construct( $this->assistantService = $satisfyService; $this->prepareService = $prepareService; $this->transferDtoAssembler = $transferDtoAssembler; - $this->transactionDtoAssembler = $transactionDtoAssembler; } /** @@ -102,8 +98,19 @@ public function forceTransfer(Wallet $from, Wallet $to, $amount, ?array $meta = $fee = $this->walletService->fee($to, $amount); $amount = max(0, $this->math->sub($amount, $discount)); - $withdraw = $this->makeOperation($from, Transaction::TYPE_WITHDRAW, $this->math->add($amount, $fee, $from->decimal_places), $meta); - $deposit = $this->makeOperation($to, Transaction::TYPE_DEPOSIT, $amount, $meta); + + $withdrawDto = $this->prepareService->withdraw($from, $this->math->add($amount, $fee, $from->decimal_places), $meta); + $depositDto = $this->prepareService->deposit($to, $amount, $meta); + $transactions = $this->applyOperations( + [$withdrawDto->getWalletId() => $from, $depositDto->getWalletId() => $to], + [$withdrawDto, $depositDto], + ); + + $withdraw = $transactions[$withdrawDto->getUuid()] ?? null; + assert($withdraw !== null); + + $deposit = $transactions[$depositDto->getUuid()] ?? null; + assert($deposit !== null); $transfers = $this->multiBrings([ app(Bring::class) @@ -203,6 +210,9 @@ public function addBalance(Wallet $wallet, $amount): bool }); } + /** + * @param float|int|string $amount + */ public function makeOperation(Wallet $wallet, string $type, $amount, ?array $meta, bool $confirmed = true): Transaction { assert(in_array($type, [Transaction::TYPE_DEPOSIT, Transaction::TYPE_WITHDRAW], true)); diff --git a/src/Traits/HasWallet.php b/src/Traits/HasWallet.php index 9d2d131ca..b163e3d2b 100644 --- a/src/Traits/HasWallet.php +++ b/src/Traits/HasWallet.php @@ -158,7 +158,7 @@ public function withdraw($amount, ?array $meta = null, bool $confirmed = true): /** * Checks if you can withdraw funds. * - * @param int|string $amount + * @param float|int|string $amount */ public function canWithdraw($amount, bool $allowZero = false): bool { From 5f9bc3bb489368f257d8b19b78ba8c27a3d12231 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Mon, 8 Nov 2021 21:03:28 +0300 Subject: [PATCH 31/51] update addBalance method --- src/Services/CommonService.php | 25 ++++++++----------------- tests/BalanceTest.php | 5 +++-- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index c0413d981..98302484b 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -186,27 +186,18 @@ public function addBalance(Wallet $wallet, $amount): bool /** @var WalletModel $wallet */ $walletObject = $this->walletService->getWallet($wallet); $balance = $this->bookkeeper->increase($walletObject, $amount); + $result = 0; try { - $result = $wallet->newQuery() - ->whereKey($wallet->getKey()) - ->update(compact('balance')) - ; - } catch (Throwable $throwable) { - $this->bookkeeper->sync($walletObject, $wallet->getAvailableBalance()); - - throw $throwable; - } - - if ($result) { - $wallet->fill(compact('balance')) - ->syncOriginalAttributes('balance') - ; - } else { - $this->bookkeeper->sync($walletObject, $wallet->getAvailableBalance()); + $result = $walletObject->newQuery()->update(compact('balance')); + $walletObject->fill(compact('balance'))->syncOriginalAttribute('balance'); + } finally { + if ($result === 0) { + $this->bookkeeper->missing($walletObject); + } } - return $result; + return (bool) $result; }); } diff --git a/tests/BalanceTest.php b/tests/BalanceTest.php index 07b98a587..2738d5010 100644 --- a/tests/BalanceTest.php +++ b/tests/BalanceTest.php @@ -154,13 +154,14 @@ public function testFailUpdate(): void /** @var MockObject|Wallet $mockQuery */ $mockQuery = $this->createMock(\get_class($wallet->newQuery())); - $mockQuery->method('whereKey')->willReturn($mockQuery); - $mockQuery->method('update')->willReturn(false); + $mockQuery->method('update')->willReturn(0); /** @var MockObject|Wallet $mockWallet */ $mockWallet = $this->createMock(\get_class($wallet)); $mockWallet->method('newQuery')->willReturn($mockQuery); $mockWallet->method('getKey')->willReturn($wallet->getKey()); + $mockWallet->method('fill')->willReturn($mockWallet); + $mockWallet->method('syncOriginalAttribute')->willReturn($mockWallet); $result = app(CommonService::class) ->addBalance($mockWallet, 100) From 4f4a47a7b23603a1afd324398a2d24ef6d15ed95 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Mon, 8 Nov 2021 21:11:09 +0300 Subject: [PATCH 32/51] add new unit --- tests/WalletTest.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/WalletTest.php b/tests/WalletTest.php index a31ee473b..59365f274 100644 --- a/tests/WalletTest.php +++ b/tests/WalletTest.php @@ -62,6 +62,17 @@ public function testInvalidDeposit(): void $user->deposit(-1); } + public function testInvalidWithdraw(): void + { + $this->expectException(AmountInvalid::class); + $this->expectExceptionMessageStrict(trans('wallet::errors.price_positive')); + + /** @var User $user */ + $user = UserFactory::new()->create(); + $user->deposit(1); + $user->withdraw(-1); + } + public function testFindUserByExistsWallet(): void { /** @var Collection|User[] $users */ @@ -122,7 +133,7 @@ public function testWithdraw(): void $user->withdraw(1); } - public function testInvalidWithdraw(): void + public function testBalanceIsEmptyWithdraw(): void { $this->expectException(BalanceIsEmpty::class); $this->expectExceptionMessageStrict(trans('wallet::errors.wallet_empty')); From ea9b80c1e87a360927189d73712df9d9d36cc572 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Mon, 8 Nov 2021 23:54:48 +0300 Subject: [PATCH 33/51] Trying to add lazy funds transfers --- src/Internal/Dto/TransferLazyDto.php | 64 +++++++++++++++++++++++++ src/Internal/Service/PrepareService.php | 25 ++++++++++ src/Services/CommonService.php | 33 +++++++------ 3 files changed, 105 insertions(+), 17 deletions(-) create mode 100644 src/Internal/Dto/TransferLazyDto.php diff --git a/src/Internal/Dto/TransferLazyDto.php b/src/Internal/Dto/TransferLazyDto.php new file mode 100644 index 000000000..fe0811e47 --- /dev/null +++ b/src/Internal/Dto/TransferLazyDto.php @@ -0,0 +1,64 @@ +fromWallet = $fromWallet; + $this->toWallet = $toWallet; + $this->discount = $discount; + $this->fee = $fee; + + $this->withdrawDto = $withdrawDto; + $this->depositDto = $depositDto; + } + + public function getFromWallet(): Wallet + { + return $this->fromWallet; + } + + public function getToWallet(): Wallet + { + return $this->toWallet; + } + + public function getDiscount(): int + { + return $this->discount; + } + + public function getFee(): string + { + return $this->fee; + } + + public function getWithdrawDto(): TransactionDto + { + return $this->withdrawDto; + } + + public function getDepositDto(): TransactionDto + { + return $this->depositDto; + } +} diff --git a/src/Internal/Service/PrepareService.php b/src/Internal/Service/PrepareService.php index 292e1ae18..18ce0b771 100644 --- a/src/Internal/Service/PrepareService.php +++ b/src/Internal/Service/PrepareService.php @@ -8,15 +8,18 @@ use Bavix\Wallet\Internal\Assembler\TransactionDtoAssembler; use Bavix\Wallet\Internal\Assembler\TransferDtoAssembler; use Bavix\Wallet\Internal\Dto\TransactionDto; +use Bavix\Wallet\Internal\Dto\TransferLazyDto; use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Services\ConsistencyService; use Bavix\Wallet\Services\MathService; +use Bavix\Wallet\Services\WalletService; class PrepareService { private TransactionDtoAssembler $transactionDtoAssembler; private TransferDtoAssembler $transferDtoAssembler; private ConsistencyService $consistencyService; + private WalletService $walletService; private CastService $castService; private MathService $mathService; @@ -24,12 +27,14 @@ public function __construct( TransactionDtoAssembler $transactionDtoAssembler, TransferDtoAssembler $transferDtoAssembler, ConsistencyService $consistencyService, + WalletService $walletService, CastService $castService, MathService $mathService ) { $this->transactionDtoAssembler = $transactionDtoAssembler; $this->transferDtoAssembler = $transferDtoAssembler; $this->consistencyService = $consistencyService; + $this->walletService = $walletService; $this->castService = $castService; $this->mathService = $mathService; } @@ -61,4 +66,24 @@ public function withdraw(Wallet $wallet, string $amount, ?array $meta, bool $con $meta ); } + + public function transferLazy(Wallet $from, Wallet $to, $amount, ?array $meta = null): TransferLazyDto + { + $discount = $this->walletService->discount($from, $to); + $from = $this->walletService->getWallet($from); + $fee = (string) $this->walletService->fee($to, $amount); + + // replace max => mathService.max + $depositAmount = (string) max(0, $this->mathService->sub($amount, $discount)); + $withdrawAmount = $this->mathService->add($depositAmount, $fee, $from->decimal_places); + + return new TransferLazyDto( + $from, + $to, + $discount, + $fee, + $this->withdraw($from, $withdrawAmount, $meta), + $this->deposit($to, $depositAmount, $meta) + ); + } } diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 98302484b..ae1c37394 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -2,7 +2,6 @@ namespace Bavix\Wallet\Services; -use function app; use Bavix\Wallet\Exceptions\AmountInvalid; use Bavix\Wallet\Exceptions\BalanceIsEmpty; use Bavix\Wallet\Exceptions\InsufficientFunds; @@ -93,14 +92,10 @@ public function transfer(Wallet $from, Wallet $to, $amount, ?array $meta = null, public function forceTransfer(Wallet $from, Wallet $to, $amount, ?array $meta = null, string $status = Transfer::STATUS_TRANSFER): Transfer { return $this->lockService->lock($this, __FUNCTION__, function () use ($from, $to, $amount, $meta, $status) { - $from = $this->walletService->getWallet($from); - $discount = $this->walletService->discount($from, $to); - $fee = $this->walletService->fee($to, $amount); - - $amount = max(0, $this->math->sub($amount, $discount)); + $transferLazyDto = $this->prepareService->transferLazy($from, $to, $amount, $meta); + $withdrawDto = $transferLazyDto->getWithdrawDto(); + $depositDto = $transferLazyDto->getDepositDto(); - $withdrawDto = $this->prepareService->withdraw($from, $this->math->add($amount, $fee, $from->decimal_places), $meta); - $depositDto = $this->prepareService->deposit($to, $amount, $meta); $transactions = $this->applyOperations( [$withdrawDto->getWalletId() => $from, $depositDto->getWalletId() => $to], [$withdrawDto, $depositDto], @@ -112,15 +107,17 @@ public function forceTransfer(Wallet $from, Wallet $to, $amount, ?array $meta = $deposit = $transactions[$depositDto->getUuid()] ?? null; assert($deposit !== null); - $transfers = $this->multiBrings([ - app(Bring::class) - ->setStatus($status) - ->setDeposit($deposit) - ->setWithdraw($withdraw) - ->setDiscount($discount) - ->setFrom($from) - ->setTo($to), - ]); + $transfer = $this->transferDtoAssembler->create( + $deposit->getKey(), + $withdraw->getKey(), + $status, + $this->castService->getModel($transferLazyDto->getFromWallet()), + $this->castService->getModel($transferLazyDto->getToWallet()), + $transferLazyDto->getDiscount(), + $transferLazyDto->getFee() + ); + + $transfers = $this->atmService->makeTransfers([$transfer]); return current($transfers); }); @@ -238,6 +235,8 @@ public function applyOperations(array $wallets, array $objects): array assert($wallet !== null); $object = $this->walletService->getWallet($wallet); + assert((int) $object->getKey() === $walletId); + $balance = $this->bookkeeper->increase($object, $total); $object->newQuery()->update(compact('balance')); // ?qN From a75e745882cbd511eda9422077060820382fa042 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Tue, 9 Nov 2021 00:03:16 +0300 Subject: [PATCH 34/51] drop Bring object --- .phpstorm.meta.php | 2 - src/Objects/Bring.php | 149 --------------------------------- src/Services/CommonService.php | 50 ----------- src/Traits/CanExchange.php | 27 +++--- src/Traits/HasGift.php | 26 +++--- src/WalletServiceProvider.php | 2 - tests/SingletonTest.php | 6 -- 7 files changed, 32 insertions(+), 230 deletions(-) delete mode 100644 src/Objects/Bring.php diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index e62a0a84a..4700c737e 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -8,7 +8,6 @@ use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Models\Wallet; - use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Objects\Cart; use Bavix\Wallet\Objects\EmptyLock; use Bavix\Wallet\Objects\Operation; @@ -17,7 +16,6 @@ override(\app(0), map([ Cart::class => Cart::class, - Bring::class => Bring::class, ExchangeService::class => ExchangeService::class, CommonService::class => CommonService::class, WalletService::class => WalletService::class, diff --git a/src/Objects/Bring.php b/src/Objects/Bring.php deleted file mode 100644 index 2ca8c352e..000000000 --- a/src/Objects/Bring.php +++ /dev/null @@ -1,149 +0,0 @@ -math = $math; - } - - public function getStatus(): string - { - return $this->status; - } - - /** - * @return static - */ - public function setStatus(string $status): self - { - $this->status = $status; - - return $this; - } - - /** - * @return static - */ - public function setDiscount(int $discount): self - { - $this->discount = $this->math->round($discount); - - return $this; - } - - public function getFrom(): Wallet - { - return $this->from; - } - - /** - * @return static - */ - public function setFrom(Wallet $from): self - { - $this->from = $from; - - return $this; - } - - public function getTo(): Wallet - { - return $this->to; - } - - /** - * @return static - */ - public function setTo(Wallet $to): self - { - $this->to = $to; - - return $this; - } - - public function getDeposit(): Transaction - { - return $this->deposit; - } - - /** - * @return static - */ - public function setDeposit(Transaction $deposit): self - { - $this->deposit = $deposit; - - return $this; - } - - public function getWithdraw(): Transaction - { - return $this->withdraw; - } - - /** - * @return static - */ - public function setWithdraw(Transaction $withdraw): self - { - $this->withdraw = $withdraw; - - return $this; - } - - public function getDiscount(): int - { - return (int) $this->discount; - } - - public function getFee(): int - { - $fee = $this->fee; - if ($fee === null) { - $fee = $this->math->round( - $this->math->sub( - $this->math->abs($this->getWithdraw()->amount), - $this->math->abs($this->getDeposit()->amount) - ) - ); - } - - return (int) $fee; - } - - /** - * @param int $fee - * - * @return Bring - */ - public function setFee($fee): self - { - $this->fee = $this->math->round($fee); - - return $this; - } -} diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index ae1c37394..7e8d0c1fb 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -18,7 +18,6 @@ use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Models\Wallet as WalletModel; -use Bavix\Wallet\Objects\Bring; use function compact; use function max; use Throwable; @@ -123,55 +122,6 @@ public function forceTransfer(Wallet $from, Wallet $to, $amount, ?array $meta = }); } - /** - * Create Bring with DB::transaction. - * - * @param Bring[] $brings - * - * @deprecated - * @see AtmService::makeTransfers() - */ - public function assemble(array $brings): array - { - return $this->lockService->lock($this, __FUNCTION__, function () use ($brings) { - $self = $this; - - return $this->dbService->transaction(static function () use ($self, $brings) { - return $self->multiBrings($brings); - }); - }); - } - - /** - * Create Bring without DB::transaction. - * - * @param non-empty-array $brings - * - * @deprecated - * @see AtmService::makeTransfers() - */ - public function multiBrings(array $brings): array - { - return $this->lockService->lock($this, __FUNCTION__, function () use ($brings) { - $objects = []; - foreach ($brings as $bring) { - $object = $this->transferDtoAssembler->create( - $bring->getDeposit()->getKey(), - $bring->getWithdraw()->getKey(), - $bring->getStatus(), - $this->castService->getModel($bring->getFrom()), - $this->castService->getModel($bring->getTo()), - $bring->getDiscount(), - $bring->getFee() - ); - - $objects[$object->getUuid()] = $object; - } - - return $this->atmService->makeTransfers($objects); - }); - } - /** * @param int|string $amount * diff --git a/src/Traits/CanExchange.php b/src/Traits/CanExchange.php index 36fa0b961..7de0d662a 100644 --- a/src/Traits/CanExchange.php +++ b/src/Traits/CanExchange.php @@ -3,12 +3,14 @@ namespace Bavix\Wallet\Traits; use Bavix\Wallet\Interfaces\Wallet; +use Bavix\Wallet\Internal\Assembler\TransferDtoAssembler; use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\ExchangeInterface; use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Internal\Service\AtmService; +use Bavix\Wallet\Internal\Service\CastService; use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; -use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Services\CommonService; use Bavix\Wallet\Services\DbService; use Bavix\Wallet\Services\LockService; @@ -67,16 +69,19 @@ public function forceExchange(Wallet $to, $amount, ?array $meta = null): Transfe ->makeOperation($to, Transaction::TYPE_DEPOSIT, $math->floor($math->mul($amount, $rate, 1)), $meta) ; - $transfers = app(CommonService::class)->multiBrings([ - app(Bring::class) - ->setDiscount(0) - ->setStatus(Transfer::STATUS_EXCHANGE) - ->setDeposit($deposit) - ->setWithdraw($withdraw) - ->setFrom($from) - ->setFee($fee) - ->setTo($to), - ]); + $castService = app(CastService::class); + + $transfer = app(TransferDtoAssembler::class)->create( + $deposit->getKey(), + $withdraw->getKey(), + Transfer::STATUS_EXCHANGE, + $castService->getModel($from), + $castService->getModel($to), + 0, + $fee + ); + + $transfers = app(AtmService::class)->makeTransfers([$transfer]); return current($transfers); }); diff --git a/src/Traits/HasGift.php b/src/Traits/HasGift.php index ae77ba5a8..793546523 100644 --- a/src/Traits/HasGift.php +++ b/src/Traits/HasGift.php @@ -9,11 +9,13 @@ use Bavix\Wallet\Interfaces\Customer; use Bavix\Wallet\Interfaces\Product; use Bavix\Wallet\Interfaces\Wallet; +use Bavix\Wallet\Internal\Assembler\TransferDtoAssembler; use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Internal\Service\AtmService; +use Bavix\Wallet\Internal\Service\CastService; use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; -use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Services\CommonService; use Bavix\Wallet\Services\DbService; use Bavix\Wallet\Services\LockService; @@ -87,15 +89,19 @@ public function gift(Wallet $to, Product $product, bool $force = false): Transfe ->getWallet($to) ; - $transfers = $commonService->assemble([ - app(Bring::class) - ->setStatus(Transfer::STATUS_GIFT) - ->setDiscount($discount) - ->setDeposit($deposit) - ->setWithdraw($withdraw) - ->setFrom($from) - ->setTo($product), - ]); + $castService = app(CastService::class); + + $transfer = app(TransferDtoAssembler::class)->create( + $deposit->getKey(), + $withdraw->getKey(), + Transfer::STATUS_GIFT, + $castService->getModel($from), + $castService->getModel($product), + $discount, + $fee + ); + + $transfers = app(AtmService::class)->makeTransfers([$transfer]); return current($transfers); }); diff --git a/src/WalletServiceProvider.php b/src/WalletServiceProvider.php index e4e88982c..81556af40 100644 --- a/src/WalletServiceProvider.php +++ b/src/WalletServiceProvider.php @@ -17,7 +17,6 @@ use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Models\Wallet; -use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Objects\Cart; use Bavix\Wallet\Services\AtomicService; use Bavix\Wallet\Services\BasketService; @@ -128,7 +127,6 @@ private function bindObjects(): void $this->app->bind(Wallet::class, config('wallet.wallet.model', Wallet::class)); // object's - $this->app->bind(Bring::class, config('wallet.objects.bring', Bring::class)); $this->app->bind(Cart::class, config('wallet.objects.cart', Cart::class)); } } diff --git a/tests/SingletonTest.php b/tests/SingletonTest.php index c5d848519..41eaeb6d9 100644 --- a/tests/SingletonTest.php +++ b/tests/SingletonTest.php @@ -3,7 +3,6 @@ namespace Bavix\Wallet\Test; use Bavix\Wallet\Internal\MathInterface; -use Bavix\Wallet\Objects\Bring; use Bavix\Wallet\Objects\Cart; use Bavix\Wallet\Services\CommonService; use Bavix\Wallet\Services\DbService; @@ -18,11 +17,6 @@ */ class SingletonTest extends TestCase { - public function testBring(): void - { - self::assertNotEquals($this->getRefId(Bring::class), $this->getRefId(Bring::class)); - } - public function testCart(): void { self::assertNotEquals($this->getRefId(Cart::class), $this->getRefId(Cart::class)); From e15fec4192982a7efce6d95d6c9103827fd49b09 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Tue, 9 Nov 2021 11:28:00 +0300 Subject: [PATCH 35/51] ecs parallel --- ecs.php | 1 + 1 file changed, 1 insertion(+) diff --git a/ecs.php b/ecs.php index b778fe10e..6a6d6d5e9 100644 --- a/ecs.php +++ b/ecs.php @@ -20,6 +20,7 @@ //$services->set(DeclareStrictTypesFixer::class); $parameters = $containerConfigurator->parameters(); + $parameters->set(Option::PARALLEL, true); $parameters->set(Option::PATHS, [ __DIR__ . '/database', __DIR__ . '/src', From 4bb35c426e42b40db2df0a9d91dadd51d97c0474 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Tue, 9 Nov 2021 20:08:15 +0300 Subject: [PATCH 36/51] update .phpstorm.meta.php --- .phpstorm.meta.php | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index 4700c737e..11a47161b 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -2,26 +2,45 @@ namespace PHPSTORM_META { - use Bavix\Wallet\Interfaces\Mathable; - use Bavix\Wallet\Interfaces\Rateable; - use Bavix\Wallet\Interfaces\Storable; - use Bavix\Wallet\Models\Transaction; - use Bavix\Wallet\Models\Transfer; - use Bavix\Wallet\Models\Wallet; + use app; + use Bavix\Wallet\Internal\BasketInterface; + use Bavix\Wallet\Internal\BookkeeperInterface; + use Bavix\Wallet\Internal\CartInterface; + use Bavix\Wallet\Internal\ConsistencyInterface; + use Bavix\Wallet\Internal\ExchangeInterface; + use Bavix\Wallet\Internal\LockInterface; + use Bavix\Wallet\Internal\MathInterface; + use Bavix\Wallet\Internal\PurchaseInterface; + use Bavix\Wallet\Internal\StorageInterface; + use Bavix\Wallet\Internal\UuidInterface; use Bavix\Wallet\Objects\Cart; - use Bavix\Wallet\Objects\EmptyLock; - use Bavix\Wallet\Objects\Operation; use Bavix\Wallet\Services\CommonService; + use Bavix\Wallet\Services\DbService; + use Bavix\Wallet\Services\LockService; + use Bavix\Wallet\Services\MetaService; use Bavix\Wallet\Services\WalletService; - override(\app(0), map([ + override(app(0), map([ + BasketInterface::class => BasketInterface::class, + BookkeeperInterface::class => BookkeeperInterface::class, + CartInterface::class => CartInterface::class, + ConsistencyInterface::class => ConsistencyInterface::class, + ExchangeInterface::class => ExchangeInterface::class, + LockInterface::class => LockInterface::class, + MathInterface::class => MathInterface::class, + PurchaseInterface::class => PurchaseInterface::class, + StorageInterface::class => StorageInterface::class, + UuidInterface::class => UuidInterface::class, + + // deprecated's Cart::class => Cart::class, - ExchangeService::class => ExchangeService::class, + + // old CommonService::class => CommonService::class, + DbService::class => DbService::class, + LockService::class => LockService::class, + MetaService::class => MetaService::class, WalletService::class => WalletService::class, - Wallet::class => Wallet::class, - Transfer::class => Transfer::class, - Transaction::class => Transaction::class, ])); } From e0a20eb1eda81a90dd5faa32144d0c1a32b9417c Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Tue, 9 Nov 2021 23:49:52 +0300 Subject: [PATCH 37/51] drop support casts in models --- config/config.php | 93 +++++++------------ ecs.php | 2 + src/Models/Transaction.php | 12 --- src/Models/Transfer.php | 12 --- src/Models/Wallet.php | 12 --- src/Services/AtomicService.php | 2 +- .../ExchangeService.php} | 4 +- src/Services/MathService.php | 2 +- src/WalletServiceProvider.php | 62 ++++++------- tests/CastsTest.php | 67 ------------- tests/Common/MyExchange.php | 10 +- tests/ExchangeTest.php | 4 +- tests/RaceCondition.php | 2 - tests/TestCase.php | 6 +- 14 files changed, 75 insertions(+), 215 deletions(-) rename src/{Simple/Exchange.php => Services/ExchangeService.php} (72%) delete mode 100644 tests/CastsTest.php diff --git a/config/config.php b/config/config.php index 1895d7906..ac06b140a 100644 --- a/config/config.php +++ b/config/config.php @@ -3,96 +3,67 @@ use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Models\Wallet; -use Bavix\Wallet\Objects\Bring; -use Bavix\Wallet\Objects\Cart; -use Bavix\Wallet\Objects\Operation; +use Bavix\Wallet\Services\AtomicService; +use Bavix\Wallet\Services\BasketService; +use Bavix\Wallet\Services\BookkeeperService; use Bavix\Wallet\Services\CommonService; +use Bavix\Wallet\Services\ConsistencyService; +use Bavix\Wallet\Services\DbService; +use Bavix\Wallet\Services\ExchangeService; use Bavix\Wallet\Services\LockService; use Bavix\Wallet\Services\MathService; +use Bavix\Wallet\Services\MetaService; +use Bavix\Wallet\Services\PurchaseService; +use Bavix\Wallet\Services\StorageService; +use Bavix\Wallet\Services\UuidFactoryService; use Bavix\Wallet\Services\WalletService; -use Bavix\Wallet\Simple\Exchange; return [ - /** - * This parameter is necessary for more accurate calculations. - * PS, Arbitrary Precision Calculations. - */ - 'math' => [ - 'scale' => 64, - ], - - /** - * The parameter is used for fast packet overload. - * You do not need to search for the desired class by code, the library will do it itself. - */ - 'package' => [ - 'exchange' => Exchange::class, - 'mathable' => MathService::class, - ], - - /** - * Lock settings for highload projects. - * - * If you want to replace the default cache with another, - * then write the name of the driver cache in the key `wallet.lock.cache`. - * @see https://laravel.com/docs/6.x/cache#driver-prerequisites - * - * @example - * 'cache' => 'redis' - */ + // infra settings + 'cache' => ['driver' => null], 'lock' => [ - 'cache' => null, - 'enabled' => false, + 'driver' => null, 'seconds' => 1, ], - /** - * Services are the main core of the library and sometimes they need to be improved. - * This configuration will help you to quickly customize the library. - */ + // long arithmetic + 'math_scale' => 64, + + // service overload 'services' => [ + 'basket' => BasketService::class, + 'bookkeeper' => BookkeeperService::class, + 'consistency' => ConsistencyService::class, + 'exchange' => ExchangeService::class, + 'atomic' => AtomicService::class, + 'math' => MathService::class, + 'purchase' => PurchaseService::class, + 'storage' => StorageService::class, + 'uuid' => UuidFactoryService::class, + ], + + // legacy-service overload + 'legacy' => [ 'common' => CommonService::class, 'wallet' => WalletService::class, + 'db' => DbService::class, 'lock' => LockService::class, + 'meta' => MetaService::class, ], - 'objects' => [ - 'bring' => Bring::class, - 'cart' => Cart::class, - 'operation' => Operation::class, - ], - - /** - * Transaction model configuration. - */ 'transaction' => [ 'table' => 'transactions', 'model' => Transaction::class, - 'casts' => [ - 'amount' => 'string', - ], ], - /** - * Transfer model configuration. - */ 'transfer' => [ 'table' => 'transfers', 'model' => Transfer::class, - 'casts' => [ - 'fee' => 'string', - ], ], - /** - * Wallet model configuration. - */ 'wallet' => [ 'table' => 'wallets', 'model' => Wallet::class, - 'casts' => [ - 'balance' => 'string', - ], 'creating' => [], 'default' => [ 'name' => 'Default Wallet', diff --git a/ecs.php b/ecs.php index 6a6d6d5e9..459b5f571 100644 --- a/ecs.php +++ b/ecs.php @@ -22,7 +22,9 @@ $parameters = $containerConfigurator->parameters(); $parameters->set(Option::PARALLEL, true); $parameters->set(Option::PATHS, [ + __DIR__ . '/config', __DIR__ . '/database', + __DIR__ . '/resources/lang', __DIR__ . '/src', __DIR__ . '/tests', ]); diff --git a/src/Models/Transaction.php b/src/Models/Transaction.php index 81ef732e0..6274bc068 100644 --- a/src/Models/Transaction.php +++ b/src/Models/Transaction.php @@ -4,7 +4,6 @@ namespace Bavix\Wallet\Models; -use function array_merge; use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Models\Wallet as WalletModel; @@ -57,17 +56,6 @@ class Transaction extends Model 'meta' => 'json', ]; - /** - * {@inheritdoc} - */ - public function getCasts(): array - { - return array_merge( - parent::getCasts(), - config('wallet.transaction.casts', []) - ); - } - public function getTable(): string { if (!$this->table) { diff --git a/src/Models/Transfer.php b/src/Models/Transfer.php index ca33ca021..f812e951e 100644 --- a/src/Models/Transfer.php +++ b/src/Models/Transfer.php @@ -4,7 +4,6 @@ namespace Bavix\Wallet\Models; -use function array_merge; use function config; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; @@ -58,17 +57,6 @@ class Transfer extends Model 'withdraw_id' => 'int', ]; - /** - * {@inheritdoc} - */ - public function getCasts(): array - { - return array_merge( - parent::getCasts(), - config('wallet.transfer.casts', []) - ); - } - public function getTable(): string { if (!$this->table) { diff --git a/src/Models/Wallet.php b/src/Models/Wallet.php index 2bc197106..3322e0e32 100644 --- a/src/Models/Wallet.php +++ b/src/Models/Wallet.php @@ -6,7 +6,6 @@ use function app; use function array_key_exists; -use function array_merge; use Bavix\Wallet\Interfaces\Confirmable; use Bavix\Wallet\Interfaces\Customer; use Bavix\Wallet\Interfaces\Exchangeable; @@ -70,17 +69,6 @@ class Wallet extends Model implements Customer, WalletFloat, Confirmable, Exchan 'decimal_places' => 2, ]; - /** - * {@inheritdoc} - */ - public function getCasts(): array - { - return array_merge( - parent::getCasts(), - config('wallet.wallet.casts', []) - ); - } - public function getTable(): string { if (!$this->table) { diff --git a/src/Services/AtomicService.php b/src/Services/AtomicService.php index 628bd9e18..28fae0271 100644 --- a/src/Services/AtomicService.php +++ b/src/Services/AtomicService.php @@ -23,7 +23,7 @@ public function __construct( ) { $this->seconds = (int) $config->get('wallet.lock.seconds', 1); $this->cache = $cacheManager->driver( - $config->get('wallet.lock.cache', 'array') + $config->get('wallet.lock.driver', 'array') ); } diff --git a/src/Simple/Exchange.php b/src/Services/ExchangeService.php similarity index 72% rename from src/Simple/Exchange.php rename to src/Services/ExchangeService.php index 46cfb98a8..93a20300c 100644 --- a/src/Simple/Exchange.php +++ b/src/Services/ExchangeService.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace Bavix\Wallet\Simple; +namespace Bavix\Wallet\Services; use Bavix\Wallet\Internal\ExchangeInterface; -class Exchange implements ExchangeInterface +class ExchangeService implements ExchangeInterface { public function convertTo(string $fromCurrency, string $toCurrency, $amount): string { diff --git a/src/Services/MathService.php b/src/Services/MathService.php index 8671b7d26..e097a83fa 100644 --- a/src/Services/MathService.php +++ b/src/Services/MathService.php @@ -15,7 +15,7 @@ class MathService implements MathInterface public function __construct(ConfigRepository $config) { - $this->scale = (int) $config->get('wallet.math.scale', 64); + $this->scale = (int) $config->get('wallet.math_scale', 64); } /** diff --git a/src/WalletServiceProvider.php b/src/WalletServiceProvider.php index 81556af40..268c65d5f 100644 --- a/src/WalletServiceProvider.php +++ b/src/WalletServiceProvider.php @@ -17,13 +17,13 @@ use Bavix\Wallet\Models\Transaction; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Models\Wallet; -use Bavix\Wallet\Objects\Cart; use Bavix\Wallet\Services\AtomicService; use Bavix\Wallet\Services\BasketService; use Bavix\Wallet\Services\BookkeeperService; use Bavix\Wallet\Services\CommonService; use Bavix\Wallet\Services\ConsistencyService; use Bavix\Wallet\Services\DbService; +use Bavix\Wallet\Services\ExchangeService; use Bavix\Wallet\Services\LockService; use Bavix\Wallet\Services\MathService; use Bavix\Wallet\Services\MetaService; @@ -31,7 +31,6 @@ use Bavix\Wallet\Services\StorageService; use Bavix\Wallet\Services\UuidFactoryService; use Bavix\Wallet\Services\WalletService; -use Bavix\Wallet\Simple\Exchange; use function config; use function dirname; use function function_exists; @@ -58,7 +57,7 @@ public function boot(): void $this->commands([RefreshBalance::class]); if ($this->shouldMigrate()) { - $this->loadMigrationsFrom([__DIR__.'/../database']); + $this->loadMigrationsFrom([dirname(__DIR__).'/database']); } if (function_exists('config_path')) { @@ -82,51 +81,48 @@ public function register(): void 'wallet' ); - $this->singletons(); - $this->legacySingleton(); - $this->bindObjects(); + $configure = config('wallet', []); + + $this->singletons($configure['services'] ?? []); + $this->legacySingleton($configure['legacy'] ?? []); + $this->bindObjects($configure); } /** * Determine if we should register the migrations. */ - protected function shouldMigrate(): bool + private function shouldMigrate(): bool { return WalletConfigure::isRunsMigrations(); } - private function singletons(): void + private function singletons(array $configure): void { - // Bind eloquent models to IoC container - $this->app->singleton(ExchangeInterface::class, config('wallet.package.exchange', Exchange::class)); - $this->app->singleton(MathInterface::class, config('wallet.package.mathable', MathService::class)); - $this->app->singleton(CommonService::class, config('wallet.services.common', CommonService::class)); - $this->app->singleton(WalletService::class, config('wallet.services.wallet', WalletService::class)); - - $this->app->singleton(LockInterface::class, AtomicService::class); - $this->app->singleton(UuidInterface::class, UuidFactoryService::class); - $this->app->singleton(StorageInterface::class, StorageService::class); - $this->app->singleton(BookkeeperInterface::class, BookkeeperService::class); - $this->app->singleton(BasketInterface::class, BasketService::class); - $this->app->singleton(ConsistencyInterface::class, ConsistencyService::class); - $this->app->singleton(PurchaseInterface::class, PurchaseService::class); + $this->app->singleton(BasketInterface::class, $configure['basket'] ?? BasketService::class); + $this->app->singleton(BookkeeperInterface::class, $configure['bookkeeper'] ?? BookkeeperService::class); + $this->app->singleton(ConsistencyInterface::class, $configure['consistency'] ?? ConsistencyService::class); + $this->app->singleton(ExchangeInterface::class, $configure['exchange'] ?? ExchangeService::class); + $this->app->singleton(LockInterface::class, $configure['atomic'] ?? AtomicService::class); + $this->app->singleton(MathInterface::class, $configure['math'] ?? MathService::class); + $this->app->singleton(PurchaseInterface::class, $configure['purchase'] ?? PurchaseService::class); + $this->app->singleton(StorageInterface::class, $configure['storage'] ?? StorageService::class); + $this->app->singleton(UuidInterface::class, $configure['uuid'] ?? UuidFactoryService::class); } - private function legacySingleton(): void + private function legacySingleton(array $configure): void { - $this->app->singleton(DbService::class, config('wallet.services.db', DbService::class)); - $this->app->singleton(LockService::class, config('wallet.services.lock', LockService::class)); - $this->app->singleton(MetaService::class); + $this->app->singleton(CommonService::class, $configure['common'] ?? null); + $this->app->singleton(WalletService::class, $configure['wallet'] ?? null); + + $this->app->singleton(DbService::class, $configure['db'] ?? null); + $this->app->singleton(LockService::class, $configure['lock'] ?? null); + $this->app->singleton(MetaService::class, $configure['meta'] ?? null); } - private function bindObjects(): void + private function bindObjects(array $configure): void { - // models - $this->app->bind(Transaction::class, config('wallet.transaction.model', Transaction::class)); - $this->app->bind(Transfer::class, config('wallet.transfer.model', Transfer::class)); - $this->app->bind(Wallet::class, config('wallet.wallet.model', Wallet::class)); - - // object's - $this->app->bind(Cart::class, config('wallet.objects.cart', Cart::class)); + $this->app->bind(Transaction::class, $configure['transaction']['model'] ?? null); + $this->app->bind(Transfer::class, $configure['transfer']['model'] ?? null); + $this->app->bind(Wallet::class, $configure['wallet']['model'] ?? null); } } diff --git a/tests/CastsTest.php b/tests/CastsTest.php deleted file mode 100644 index a1c31e859..000000000 --- a/tests/CastsTest.php +++ /dev/null @@ -1,67 +0,0 @@ -create(); - self::assertEquals($buyer->balance, 0); - - self::assertIsInt($buyer->wallet->getKey()); - self::assertEquals($buyer->wallet->getCasts()['id'], 'int'); - - config(['wallet.wallet.casts.id' => 'string']); - self::assertIsString($buyer->wallet->getKey()); - self::assertEquals($buyer->wallet->getCasts()['id'], 'string'); - } - - public function testModelTransfer(): void - { - /** - * @var Buyer $buyer - * @var User $user - */ - $buyer = BuyerFactory::new()->create(); - $user = UserFactory::new()->create(); - self::assertEquals($buyer->balance, 0); - self::assertEquals($user->balance, 0); - - $deposit = $user->deposit(1000); - self::assertEquals($user->balance, $deposit->amount); - - $transfer = $user->transfer($buyer, 700); - - self::assertIsInt($transfer->getKey()); - self::assertEquals($transfer->getCasts()['id'], 'int'); - - config(['wallet.transfer.casts.id' => 'string']); - self::assertIsString($transfer->getKey()); - self::assertEquals($transfer->getCasts()['id'], 'string'); - } - - public function testModelTransaction(): void - { - /** @var Buyer $buyer */ - $buyer = BuyerFactory::new()->create(); - self::assertEquals($buyer->balance, 0); - $deposit = $buyer->deposit(1); - - self::assertIsInt($deposit->getKey()); - self::assertEquals($deposit->getCasts()['id'], 'int'); - - config(['wallet.transaction.casts.id' => 'string']); - self::assertIsString($deposit->getKey()); - self::assertEquals($deposit->getCasts()['id'], 'string'); - } -} diff --git a/tests/Common/MyExchange.php b/tests/Common/MyExchange.php index 22d08edb8..71cdc9fb6 100644 --- a/tests/Common/MyExchange.php +++ b/tests/Common/MyExchange.php @@ -8,25 +8,25 @@ class MyExchange implements ExchangeInterface { + private MathInterface $mathService; + private array $rates = [ 'USD' => [ 'RUB' => 67.61, ], ]; - private MathInterface $math; - /** * Rate constructor. */ public function __construct(MathInterface $mathService) { - $this->math = $mathService; + $this->mathService = $mathService; foreach ($this->rates as $from => $rates) { foreach ($rates as $to => $rate) { if (empty($this->rates[$to][$from])) { - $this->rates[$to][$from] = $this->math->div(1, $rate); + $this->rates[$to][$from] = $this->mathService->div(1, $rate); } } } @@ -35,7 +35,7 @@ public function __construct(MathInterface $mathService) /** @param float|int|string $amount */ public function convertTo(string $fromCurrency, string $toCurrency, $amount): string { - return $this->math->mul($amount, Arr::get( + return $this->mathService->mul($amount, Arr::get( Arr::get($this->rates, $fromCurrency, []), $toCurrency, 1 diff --git a/tests/ExchangeTest.php b/tests/ExchangeTest.php index ad4ab17b0..841bd3310 100644 --- a/tests/ExchangeTest.php +++ b/tests/ExchangeTest.php @@ -4,7 +4,7 @@ use Bavix\Wallet\Internal\ExchangeInterface; use Bavix\Wallet\Models\Transfer; -use Bavix\Wallet\Simple\Exchange; +use Bavix\Wallet\Services\ExchangeService; use Bavix\Wallet\Test\Factories\UserMultiFactory; use Bavix\Wallet\Test\Models\UserMulti; use Illuminate\Support\Str; @@ -72,7 +72,7 @@ public function testSafe(): void public function testExchangeClass(): void { - $service = app(Exchange::class); + $service = app(ExchangeService::class); self::assertEquals(1, $service->convertTo('USD', 'EUR', 1)); self::assertEquals(5, $service->convertTo('USD', 'EUR', 5)); diff --git a/tests/RaceCondition.php b/tests/RaceCondition.php index 7788a3d66..533d2fcb3 100644 --- a/tests/RaceCondition.php +++ b/tests/RaceCondition.php @@ -22,8 +22,6 @@ public function enableRaceCondition(): bool $this->refreshApplication(); } - $this->app['config']->set('wallet.lock.enabled', extension_loaded('memcached')); - return true; } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 8922d231d..5713f4918 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,7 +3,6 @@ namespace Bavix\Wallet\Test; use Bavix\Wallet\Internal\StorageInterface; -use Bavix\Wallet\Services\MathService; use Bavix\Wallet\Test\Common\Models\Transaction; use Bavix\Wallet\Test\Common\Models\Transfer; use Bavix\Wallet\Test\Common\Models\Wallet; @@ -48,8 +47,7 @@ protected function updateConfig(Application $app): void $config = $app['config']; // Bind eloquent models to IoC container - $app['config']->set('wallet.package.exchange', MyExchange::class); - $app['config']->set('wallet.package.mathable', MathService::class); + $app['config']->set('wallet.services.exchange', MyExchange::class); // database $config->set('database.connections.testing.prefix', 'tests'); @@ -72,7 +70,5 @@ protected function updateConfig(Application $app): void $config->set('wallet.transaction.model', Transaction::class); $config->set('wallet.transfer.model', Transfer::class); $config->set('wallet.wallet.model', Wallet::class); - - $config->set('wallet.lock.enabled', false); } } From 890c18980cc0ef015da1a42979a6e2465fd4f99f Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Tue, 9 Nov 2021 23:58:38 +0300 Subject: [PATCH 38/51] removed unnecessary checks that are no longer needed --- config/config.php | 2 +- src/Services/MathService.php | 2 +- tests/AtomicLockTest.php | 4 ++-- tests/BalanceLockTest.php | 11 ----------- tests/CartLockTest.php | 11 ----------- tests/ConfirmLockTest.php | 11 ----------- tests/RaceCondition.php | 27 --------------------------- 7 files changed, 4 insertions(+), 64 deletions(-) delete mode 100644 tests/BalanceLockTest.php delete mode 100644 tests/CartLockTest.php delete mode 100644 tests/ConfirmLockTest.php delete mode 100644 tests/RaceCondition.php diff --git a/config/config.php b/config/config.php index ac06b140a..db9abdd37 100644 --- a/config/config.php +++ b/config/config.php @@ -27,7 +27,7 @@ ], // long arithmetic - 'math_scale' => 64, + 'math' => ['scale' => 64], // service overload 'services' => [ diff --git a/src/Services/MathService.php b/src/Services/MathService.php index e097a83fa..8671b7d26 100644 --- a/src/Services/MathService.php +++ b/src/Services/MathService.php @@ -15,7 +15,7 @@ class MathService implements MathInterface public function __construct(ConfigRepository $config) { - $this->scale = (int) $config->get('wallet.math_scale', 64); + $this->scale = (int) $config->get('wallet.math.scale', 64); } /** diff --git a/tests/AtomicLockTest.php b/tests/AtomicLockTest.php index c4b90da2c..3fe75cb6b 100644 --- a/tests/AtomicLockTest.php +++ b/tests/AtomicLockTest.php @@ -2,7 +2,7 @@ namespace Bavix\Wallet\Test; -use Bavix\Wallet\Services\AtomicService; +use Bavix\Wallet\Internal\LockInterface; /** * @internal @@ -11,7 +11,7 @@ class AtomicLockTest extends TestCase { public function testAtomic(): void { - $atomic = app(AtomicService::class); + $atomic = app(LockInterface::class); $atomic->block('hello', static fn () => 'hello world'); $atomic->block('hello', static fn () => 'hello world'); self::assertTrue(true); diff --git a/tests/BalanceLockTest.php b/tests/BalanceLockTest.php deleted file mode 100644 index 580b03462..000000000 --- a/tests/BalanceLockTest.php +++ /dev/null @@ -1,11 +0,0 @@ -app) { - $this->refreshApplication(); - } - - return true; - } -} From c939181b279c92db57d1645087b70bd70753d100 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 10 Nov 2021 16:21:19 +0300 Subject: [PATCH 39/51] update key --- src/Services/BookkeeperService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Services/BookkeeperService.php b/src/Services/BookkeeperService.php index cabf58e24..bb9173559 100644 --- a/src/Services/BookkeeperService.php +++ b/src/Services/BookkeeperService.php @@ -64,6 +64,6 @@ public function increase(Wallet $wallet, $value): string private function getKey(Wallet $wallet): string { - return __CLASS__.'::'.$wallet->getKey(); + return __CLASS__.'::'.$wallet->uuid; } } From 50da2240dba79beba657e947873ce36af12de7d4 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 10 Nov 2021 19:35:08 +0300 Subject: [PATCH 40/51] drop units --- .phpstorm.meta.php | 3 +- src/Services/CommonService.php | 21 ++++---- src/Services/LockService.php | 25 ++++----- src/Traits/CanConfirm.php | 49 +++++------------- tests/BalanceTest.php | 92 ++-------------------------------- tests/CartTest.php | 4 +- tests/ConfirmMockTest.php | 74 --------------------------- tests/TestCase.php | 7 ++- 8 files changed, 47 insertions(+), 228 deletions(-) delete mode 100644 tests/ConfirmMockTest.php diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index 11a47161b..dc0d4e49b 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -2,7 +2,6 @@ namespace PHPSTORM_META { - use app; use Bavix\Wallet\Internal\BasketInterface; use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\CartInterface; @@ -20,7 +19,7 @@ use Bavix\Wallet\Services\MetaService; use Bavix\Wallet\Services\WalletService; - override(app(0), map([ + override(\app(0), map([ BasketInterface::class => BasketInterface::class, BookkeeperInterface::class => BookkeeperInterface::class, CartInterface::class => CartInterface::class, diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 7e8d0c1fb..184d88ca3 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -72,14 +72,12 @@ public function __construct( */ public function transfer(Wallet $from, Wallet $to, $amount, ?array $meta = null, string $status = Transfer::STATUS_TRANSFER): Transfer { - return $this->lockService->lock($this, __FUNCTION__, function () use ($from, $to, $amount, $meta, $status) { - $discount = $this->walletService->discount($from, $to); - $newAmount = max(0, $this->math->sub($amount, $discount)); - $fee = $this->walletService->fee($to, $newAmount); - $this->consistency->checkPotential($from, $this->math->add($newAmount, $fee)); + $discount = $this->walletService->discount($from, $to); + $newAmount = max(0, $this->math->sub($amount, $discount)); + $fee = $this->walletService->fee($to, $newAmount); + $this->consistency->checkPotential($from, $this->math->add($newAmount, $fee)); - return $this->forceTransfer($from, $to, $amount, $meta, $status); - }); + return $this->forceTransfer($from, $to, $amount, $meta, $status); } /** @@ -90,7 +88,7 @@ public function transfer(Wallet $from, Wallet $to, $amount, ?array $meta = null, */ public function forceTransfer(Wallet $from, Wallet $to, $amount, ?array $meta = null, string $status = Transfer::STATUS_TRANSFER): Transfer { - return $this->lockService->lock($this, __FUNCTION__, function () use ($from, $to, $amount, $meta, $status) { + return $this->dbService->transaction(function () use ($from, $to, $amount, $meta, $status) { $transferLazyDto = $this->prepareService->transferLazy($from, $to, $amount, $meta); $withdrawDto = $transferLazyDto->getWithdrawDto(); $depositDto = $transferLazyDto->getDepositDto(); @@ -136,7 +134,10 @@ public function addBalance(Wallet $wallet, $amount): bool $result = 0; try { - $result = $walletObject->newQuery()->update(compact('balance')); + $result = $walletObject->newQuery() + ->whereKey($walletObject->getKey()) + ->update(compact('balance')); + $walletObject->fill(compact('balance'))->syncOriginalAttribute('balance'); } finally { if ($result === 0) { @@ -189,7 +190,7 @@ public function applyOperations(array $wallets, array $objects): array $balance = $this->bookkeeper->increase($object, $total); - $object->newQuery()->update(compact('balance')); // ?qN + $object->newQuery()->whereKey($object->getKey())->update(compact('balance')); // ?qN $object->fill(compact('balance'))->syncOriginalAttribute('balance'); } diff --git a/src/Services/LockService.php b/src/Services/LockService.php index 561ff91e3..6860e7c9e 100644 --- a/src/Services/LockService.php +++ b/src/Services/LockService.php @@ -5,7 +5,11 @@ namespace Bavix\Wallet\Services; use Bavix\Wallet\Internal\UuidInterface; +use Closure; use Illuminate\Database\Eloquent\Model; +use ReflectionException; +use ReflectionFunction; +use function get_class; /** * @deprecated @@ -14,43 +18,34 @@ class LockService { private string $ikey; - private AtomicService $atomicService; - /** - * LockService constructor. - */ public function __construct(UuidInterface $uuidService, AtomicService $atomicService) { $this->ikey = $uuidService->uuid4(); $this->atomicService = $atomicService; } - /** - * @param object $self - * - * @return mixed - */ - public function lock($self, string $name, \Closure $closure) + public function lock(object $self, string $name, Closure $closure) { - $class = \get_class($self); + $class = get_class($self); $uniqId = $class.$this->ikey; if ($self instanceof Model) { $uniqId = $class.$self->getKey(); } return $this->atomicService->block( - "{$name}.{$uniqId}", + "legacy_{$name}.{$uniqId}", $this->bindTo($self, $closure) ); } /** - * @param object $self + * @throws ReflectionException */ - protected function bindTo($self, \Closure $closure): \Closure + protected function bindTo(object $self, Closure $closure): Closure { - $reflect = new \ReflectionFunction($closure); + $reflect = new ReflectionFunction($closure); if (strpos((string) $reflect, 'static') === false) { return $closure->bindTo($self); } diff --git a/src/Traits/CanConfirm.php b/src/Traits/CanConfirm.php index b52a584af..fd978df9e 100644 --- a/src/Traits/CanConfirm.php +++ b/src/Traits/CanConfirm.php @@ -27,25 +27,15 @@ trait CanConfirm */ public function confirm(Transaction $transaction): bool { - return app(LockService::class)->lock($this, __FUNCTION__, function () use ($transaction) { - /** @var Confirmable|Wallet $self */ - $self = $this; - - return app(DbService::class)->transaction(static function () use ($self, $transaction) { - $wallet = app(WalletService::class)->getWallet($self); - if (!$wallet->refreshBalance()) { - return false; - } - - if ($transaction->type === Transaction::TYPE_WITHDRAW) { - app(ConsistencyInterface::class)->checkPotential( - $wallet, - app(MathInterface::class)->abs($transaction->amount) - ); - } - - return $self->forceConfirm($transaction); - }); + return app(DbService::class)->transaction(function () use ($transaction) { + if ($transaction->type === Transaction::TYPE_WITHDRAW) { + app(ConsistencyInterface::class)->checkPotential( + app(WalletService::class)->getWallet($this), + app(MathInterface::class)->abs($transaction->amount) + ); + } + + return $this->forceConfirm($transaction); }); } @@ -66,19 +56,12 @@ public function safeConfirm(Transaction $transaction): bool public function resetConfirm(Transaction $transaction): bool { return app(LockService::class)->lock($this, __FUNCTION__, function () use ($transaction) { - /** @var Wallet $self */ - $self = $this; - - return app(DbService::class)->transaction(static function () use ($self, $transaction) { - $wallet = app(WalletService::class)->getWallet($self); - if (!$wallet->refreshBalance()) { - return false; - } - + return app(DbService::class)->transaction(function () use ($transaction) { if (!$transaction->confirmed) { throw new UnconfirmedInvalid(trans('wallet::errors.unconfirmed_invalid')); } + $wallet = app(WalletService::class)->getWallet($this); $mathService = app(MathInterface::class); $negativeAmount = $mathService->negative($transaction->amount); @@ -107,18 +90,12 @@ public function safeResetConfirm(Transaction $transaction): bool public function forceConfirm(Transaction $transaction): bool { return app(LockService::class)->lock($this, __FUNCTION__, function () use ($transaction) { - /** @var Wallet $self */ - $self = $this; - - return app(DbService::class)->transaction(static function () use ($self, $transaction) { - $wallet = app(WalletService::class) - ->getWallet($self) - ; - + return app(DbService::class)->transaction(function () use ($transaction) { if ($transaction->confirmed) { throw new ConfirmedInvalid(trans('wallet::errors.confirmed_invalid')); } + $wallet = app(WalletService::class)->getWallet($this); if ($wallet->getKey() !== $transaction->wallet_id) { throw new WalletOwnerInvalid(trans('wallet::errors.owner_invalid')); } diff --git a/tests/BalanceTest.php b/tests/BalanceTest.php index 2738d5010..171d1858c 100644 --- a/tests/BalanceTest.php +++ b/tests/BalanceTest.php @@ -138,39 +138,6 @@ public function testGetBalance(): void self::assertEquals(0, app(BookkeeperInterface::class)->amount($wallet)); } - /** - * @throws - */ - public function testFailUpdate(): void - { - /** @var Buyer $buyer */ - $buyer = BuyerFactory::new()->create(); - self::assertFalse($buyer->relationLoaded('wallet')); - $wallet = $buyer->wallet; - - self::assertFalse($wallet->exists); - self::assertEquals(0, $wallet->balance); - self::assertTrue($wallet->exists); - - /** @var MockObject|Wallet $mockQuery */ - $mockQuery = $this->createMock(\get_class($wallet->newQuery())); - $mockQuery->method('update')->willReturn(0); - - /** @var MockObject|Wallet $mockWallet */ - $mockWallet = $this->createMock(\get_class($wallet)); - $mockWallet->method('newQuery')->willReturn($mockQuery); - $mockWallet->method('getKey')->willReturn($wallet->getKey()); - $mockWallet->method('fill')->willReturn($mockWallet); - $mockWallet->method('syncOriginalAttribute')->willReturn($mockWallet); - - $result = app(CommonService::class) - ->addBalance($mockWallet, 100) - ; - - self::assertFalse($result); - self::assertEquals(0, app(BookkeeperInterface::class)->amount($wallet)); - } - /** * @throws */ @@ -194,62 +161,11 @@ public function testThrowUpdate(): void /** @var MockObject|Wallet $mockWallet */ $mockWallet = $this->createMock(\get_class($wallet)); + $mockWallet->method('getBalanceAttribute')->willReturn('125'); $mockWallet->method('newQuery')->willReturn($mockQuery); $mockWallet->method('getKey')->willReturn($wallet->getKey()); - app(CommonService::class) - ->addBalance($mockWallet, 100) - ; - } - - /** - * @throws - */ - public function testArtisanRefresh(): void - { - /** @var UserMulti $user */ - $user = UserMultiFactory::new()->create(); - $wallets = \range('a', 'z'); - $sums = []; - $ids = []; - foreach ($wallets as $name) { - $wallet = $user->createWallet(['name' => $name]); - $ids[] = $wallet->id; - $sums[$name] = 0; - $operations = \random_int(1, 10); - for ($i = 0; $i < $operations; ++$i) { - $amount = \random_int(10, 10000); - $confirmed = (bool) \random_int(0, 1); - $deposit = $wallet->deposit($amount, null, $confirmed); - self::assertIsInt($deposit->wallet_id); - - if ($confirmed) { - $sums[$name] += $amount; - } - - self::assertEquals($amount, $deposit->amount); - self::assertEquals($confirmed, $deposit->confirmed); - self::assertEquals($sums[$name], $wallet->balance); - } - } - - /** - * Check for the number of created wallets. - * Make sure you didn't accidentally create the default wallet. - * - * @see https://github.com/bavix/laravel-wallet/issues/218 - */ - self::assertCount(count($wallets), $user->wallets); - - // fresh balance - DB::table(config('wallet.wallet.table')) - ->update(['balance' => 0]) - ; - - $this->artisan('wallet:refresh'); - Wallet::query()->whereKey($ids)->each(function (Wallet $wallet) use ($sums) { - self::assertEquals($sums[$wallet->name], $wallet->balance); - }); + $mockWallet->newQuery()->whereKey($wallet->getKey())->update(['balance' => 100]); } public function testEqualWallet(): void @@ -294,7 +210,7 @@ public function testForceUpdate(): void * * Here is an example: */ - app(StorageInterface::class)->flush(); + app(BookkeeperInterface::class)->missing($buyer->wallet); self::assertEquals(1000, $wallet->getRawOriginal('balance')); /** @@ -338,7 +254,7 @@ public function testAdjustment(int $account, int $adjust): void * * Here is an example: */ - app(StorageInterface::class)->flush(); + app(BookkeeperInterface::class)->missing($wallet); self::assertEquals($account, $wallet->getRawOriginal('balance')); /** diff --git a/tests/CartTest.php b/tests/CartTest.php index edcb600a4..c6bca23bd 100644 --- a/tests/CartTest.php +++ b/tests/CartTest.php @@ -147,13 +147,15 @@ public function testCartQuantity(): void $cart = app(Cart::class); $amount = 0; + $price = 0; for ($i = 0; $i < count($products) - 1; ++$i) { $rnd = random_int(1, 5); $cart->addItem($products[$i], $rnd); - $buyer->deposit($products[$i]->getAmountProduct($buyer) * $rnd); + $price += $products[$i]->getAmountProduct($buyer) * $rnd; $amount += $rnd; } + $buyer->deposit($price); self::assertCount($amount, $cart->getItems()); $transfers = $buyer->payCart($cart); diff --git a/tests/ConfirmMockTest.php b/tests/ConfirmMockTest.php deleted file mode 100644 index 0c4f6a7e7..000000000 --- a/tests/ConfirmMockTest.php +++ /dev/null @@ -1,74 +0,0 @@ -create(); - $transaction = $userConfirm->deposit(100, null, false); - self::assertEquals($userConfirm->wallet->id, $transaction->wallet->id); - self::assertEquals($userConfirm->id, $transaction->payable_id); - self::assertInstanceOf(UserConfirm::class, $transaction->payable); - self::assertFalse($transaction->confirmed); - - $wallet = app(WalletService::class) - ->getWallet($userConfirm) - ; - - $mockWallet = $this->createMock(\get_class($wallet)); - $mockWallet->method('refreshBalance') - ->willReturn(false) - ; - - /** - * @var Wallet $mockWallet - */ - self::assertInstanceOf(Wallet::class, $wallet); - self::assertFalse($mockWallet->refreshBalance()); - - $userConfirm->setRelation('wallet', $mockWallet); - self::assertFalse($userConfirm->confirm($transaction)); - self::assertFalse($userConfirm->safeConfirm($transaction)); - } - - public function testFailResetConfirm(): void - { - /** @var UserConfirm $userConfirm */ - $userConfirm = UserConfirmFactory::new()->create(); - $transaction = $userConfirm->deposit(100); - self::assertEquals($userConfirm->wallet->id, $transaction->wallet->id); - self::assertEquals($userConfirm->id, $transaction->payable_id); - self::assertInstanceOf(UserConfirm::class, $transaction->payable); - self::assertTrue($transaction->confirmed); - - $wallet = app(WalletService::class) - ->getWallet($userConfirm) - ; - - $mockWallet = $this->createMock(\get_class($wallet)); - $mockWallet->method('refreshBalance') - ->willReturn(false) - ; - - /** - * @var Wallet $mockWallet - */ - self::assertInstanceOf(Wallet::class, $wallet); - self::assertFalse($mockWallet->refreshBalance()); - - $userConfirm->setRelation('wallet', $mockWallet); - self::assertFalse($userConfirm->resetConfirm($transaction)); - self::assertFalse($userConfirm->safeResetConfirm($transaction)); - } -} diff --git a/tests/TestCase.php b/tests/TestCase.php index 5713f4918..15c48a21f 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -23,7 +23,6 @@ class TestCase extends OrchestraTestCase public function setUp(): void { parent::setUp(); - app(StorageInterface::class)->flush(); } public function expectExceptionMessageStrict(string $message): void @@ -47,7 +46,7 @@ protected function updateConfig(Application $app): void $config = $app['config']; // Bind eloquent models to IoC container - $app['config']->set('wallet.services.exchange', MyExchange::class); + $config->set('wallet.services.exchange', MyExchange::class); // database $config->set('database.connections.testing.prefix', 'tests'); @@ -70,5 +69,9 @@ protected function updateConfig(Application $app): void $config->set('wallet.transaction.model', Transaction::class); $config->set('wallet.transfer.model', Transfer::class); $config->set('wallet.wallet.model', Wallet::class); + + $config->set('wallet.cache.driver', $config->get('cache.driver')); + $config->set('wallet.lock.driver', $config->get('cache.driver')); + $config->set('wallet.lock.seconds', 3); } } From 6442d006917cf95e6a5bcc451e5ae4362c60be39 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 10 Nov 2021 20:02:19 +0300 Subject: [PATCH 41/51] fix ecs --- src/Services/CommonService.php | 3 ++- src/Services/LockService.php | 2 +- src/Traits/CanConfirm.php | 2 -- tests/BalanceTest.php | 4 ---- tests/TestCase.php | 1 - 5 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 184d88ca3..3e1f216b4 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -136,7 +136,8 @@ public function addBalance(Wallet $wallet, $amount): bool try { $result = $walletObject->newQuery() ->whereKey($walletObject->getKey()) - ->update(compact('balance')); + ->update(compact('balance')) + ; $walletObject->fill(compact('balance'))->syncOriginalAttribute('balance'); } finally { diff --git a/src/Services/LockService.php b/src/Services/LockService.php index 6860e7c9e..7a8a9559e 100644 --- a/src/Services/LockService.php +++ b/src/Services/LockService.php @@ -6,10 +6,10 @@ use Bavix\Wallet\Internal\UuidInterface; use Closure; +use function get_class; use Illuminate\Database\Eloquent\Model; use ReflectionException; use ReflectionFunction; -use function get_class; /** * @deprecated diff --git a/src/Traits/CanConfirm.php b/src/Traits/CanConfirm.php index fd978df9e..008009217 100644 --- a/src/Traits/CanConfirm.php +++ b/src/Traits/CanConfirm.php @@ -7,8 +7,6 @@ use Bavix\Wallet\Exceptions\InsufficientFunds; use Bavix\Wallet\Exceptions\UnconfirmedInvalid; use Bavix\Wallet\Exceptions\WalletOwnerInvalid; -use Bavix\Wallet\Interfaces\Confirmable; -use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Models\Transaction; diff --git a/tests/BalanceTest.php b/tests/BalanceTest.php index 171d1858c..04b21e868 100644 --- a/tests/BalanceTest.php +++ b/tests/BalanceTest.php @@ -4,17 +4,13 @@ use function app; use Bavix\Wallet\Internal\BookkeeperInterface; -use Bavix\Wallet\Internal\StorageInterface; use Bavix\Wallet\Models\Wallet; use Bavix\Wallet\Services\CommonService; use Bavix\Wallet\Services\WalletService; use Bavix\Wallet\Test\Common\Services\WalletAdjustmentFailedService; use Bavix\Wallet\Test\Factories\BuyerFactory; -use Bavix\Wallet\Test\Factories\UserMultiFactory; use Bavix\Wallet\Test\Models\Buyer; -use Bavix\Wallet\Test\Models\UserMulti; use Illuminate\Database\SQLiteConnection; -use Illuminate\Support\Facades\DB; use PDOException; use PHPUnit\Framework\MockObject\MockObject; diff --git a/tests/TestCase.php b/tests/TestCase.php index 15c48a21f..dbd052787 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -2,7 +2,6 @@ namespace Bavix\Wallet\Test; -use Bavix\Wallet\Internal\StorageInterface; use Bavix\Wallet\Test\Common\Models\Transaction; use Bavix\Wallet\Test\Common\Models\Transfer; use Bavix\Wallet\Test\Common\Models\Wallet; From d9142767571972559c69ebf618e4d97819f79959 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 10 Nov 2021 21:26:57 +0300 Subject: [PATCH 42/51] update BookkeeperService --- src/Services/BookkeeperService.php | 5 +---- src/Services/StorageService.php | 4 ++-- src/Traits/HasWallet.php | 7 ++----- tests/TestCase.php | 1 - 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/Services/BookkeeperService.php b/src/Services/BookkeeperService.php index bb9173559..f8382bc69 100644 --- a/src/Services/BookkeeperService.php +++ b/src/Services/BookkeeperService.php @@ -36,10 +36,7 @@ public function amount(Wallet $wallet): string } catch (RecordNotFoundException $recordNotFoundException) { $this->lock->block( $this->getKey($wallet), - fn () => $this->storage->sync( - $this->getKey($wallet), - $wallet->getOriginalBalance(), - ), + fn () => $this->sync($wallet, $wallet->getOriginalBalance()), ); } diff --git a/src/Services/StorageService.php b/src/Services/StorageService.php index eedfba4c3..2b92a4a07 100644 --- a/src/Services/StorageService.php +++ b/src/Services/StorageService.php @@ -40,7 +40,7 @@ public function flush(): bool public function missing(string $key): bool { - return $this->cache->delete($key); + return $this->cache->forget($key); } public function get(string $key): string @@ -61,7 +61,7 @@ public function sync(string $key, $value): bool public function increase(string $key, $value): string { return $this->lock->block( - $key, + $key.'::increase', function () use ($key, $value): string { $result = $this->math->add($this->get($key), $value); $this->sync($key, $result); diff --git a/src/Traits/HasWallet.php b/src/Traits/HasWallet.php index b163e3d2b..fdded40a7 100644 --- a/src/Traits/HasWallet.php +++ b/src/Traits/HasWallet.php @@ -43,12 +43,9 @@ trait HasWallet */ public function deposit($amount, ?array $meta = null, bool $confirmed = true): Transaction { - /** @var Wallet $self */ - $self = $this; - - return app(DbService::class)->transaction(static function () use ($self, $amount, $meta, $confirmed) { + return app(DbService::class)->transaction(function () use ($amount, $meta, $confirmed) { return app(CommonService::class) - ->makeOperation($self, Transaction::TYPE_DEPOSIT, $amount, $meta, $confirmed) + ->makeOperation($this, Transaction::TYPE_DEPOSIT, $amount, $meta, $confirmed) ; }); } diff --git a/tests/TestCase.php b/tests/TestCase.php index dbd052787..ed632a09f 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -71,6 +71,5 @@ protected function updateConfig(Application $app): void $config->set('wallet.cache.driver', $config->get('cache.driver')); $config->set('wallet.lock.driver', $config->get('cache.driver')); - $config->set('wallet.lock.seconds', 3); } } From 8bae07071f5cbc1f418473cc0890c54de2ad954f Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Wed, 10 Nov 2021 23:47:11 +0300 Subject: [PATCH 43/51] - drop command - fix units --- src/Commands/RefreshBalance.php | 31 ----------------------------- src/WalletServiceProvider.php | 3 --- tests/BalanceTest.php | 35 +++++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 34 deletions(-) delete mode 100644 src/Commands/RefreshBalance.php diff --git a/src/Commands/RefreshBalance.php b/src/Commands/RefreshBalance.php deleted file mode 100644 index 85df85982..000000000 --- a/src/Commands/RefreshBalance.php +++ /dev/null @@ -1,31 +0,0 @@ -each(static fn (Wallet $wallet) => $wallet->refreshBalance()); - } -} diff --git a/src/WalletServiceProvider.php b/src/WalletServiceProvider.php index 268c65d5f..63a68a9a8 100644 --- a/src/WalletServiceProvider.php +++ b/src/WalletServiceProvider.php @@ -4,7 +4,6 @@ namespace Bavix\Wallet; -use Bavix\Wallet\Commands\RefreshBalance; use Bavix\Wallet\Internal\BasketInterface; use Bavix\Wallet\Internal\BookkeeperInterface; use Bavix\Wallet\Internal\ConsistencyInterface; @@ -54,8 +53,6 @@ public function boot(): void return; } - $this->commands([RefreshBalance::class]); - if ($this->shouldMigrate()) { $this->loadMigrationsFrom([dirname(__DIR__).'/database']); } diff --git a/tests/BalanceTest.php b/tests/BalanceTest.php index 04b21e868..3c2eb52a9 100644 --- a/tests/BalanceTest.php +++ b/tests/BalanceTest.php @@ -275,6 +275,41 @@ public function testAdjustment(int $account, int $adjust): void self::assertEquals($adjust, $wallet->getRawOriginal('balance')); } + public function testFailUpdate(): void + { + /** @var Buyer $buyer */ + $buyer = BuyerFactory::new()->create(); + self::assertFalse($buyer->relationLoaded('wallet')); + $wallet = $buyer->wallet; + + self::assertFalse($wallet->exists); + self::assertEquals(0, $wallet->balance); + self::assertTrue($wallet->exists); + + /** @var MockObject|Wallet $mockQuery */ + $mockQuery = $this->createMock(\get_class($wallet->newQuery())); + $mockQuery->method('whereKey')->willReturn($mockQuery); + $mockQuery->method('update')->willReturn(0); + + /** @var MockObject|Wallet $mockWallet */ + $mockWallet = $this->createMock(\get_class($wallet)); + $mockWallet->method('newQuery')->willReturn($mockQuery); + $mockWallet->method('getKey')->willReturn($wallet->getKey()); + $mockWallet->method('fill')->willReturn($mockWallet); + $mockWallet->method('syncOriginalAttribute')->willReturn($mockWallet); + $mockWallet->method('__get')->with('uuid')->willReturn($wallet->uuid); + + $bookkeeper = app(BookkeeperInterface::class); + $bookkeeper->sync($wallet, 100500); // init + + $result = app(CommonService::class) + ->addBalance($mockWallet, 100) + ; + + self::assertFalse($result); + self::assertSame('0', $bookkeeper->amount($wallet)); + } + /** * @dataProvider providerAdjustment */ From 9c800f22de7369f57f6a5ca81397f009256c95af Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 11 Nov 2021 08:21:00 +0300 Subject: [PATCH 44/51] update phpunits.yaml --- .github/workflows/phpunits.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/phpunits.yaml b/.github/workflows/phpunits.yaml index cc40f602e..fa30b09ad 100644 --- a/.github/workflows/phpunits.yaml +++ b/.github/workflows/phpunits.yaml @@ -122,11 +122,7 @@ jobs: - name: Run test suite run: | - if [[ $DB_CONNECTION == "testing" ]]; then - composer paraunit - else - composer unit - fi + composer parabench env: CACHE_DRIVER: ${{ matrix.caches }} DB_CONNECTION: ${{ matrix.databases }} From 3f26829f9aa2b90389e50de98eab1cd92395e087 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 11 Nov 2021 09:35:22 +0300 Subject: [PATCH 45/51] update parallel --- .github/workflows/phpunits.yaml | 15 +--------- config/config.php | 2 ++ tests/Common/WalletServiceProvider.php | 4 +-- tests/TableTest.php | 39 ++++++++++++++++++++++++++ tests/TestCase.php | 33 ++++++++-------------- 5 files changed, 54 insertions(+), 39 deletions(-) create mode 100644 tests/TableTest.php diff --git a/.github/workflows/phpunits.yaml b/.github/workflows/phpunits.yaml index fa30b09ad..534bd99da 100644 --- a/.github/workflows/phpunits.yaml +++ b/.github/workflows/phpunits.yaml @@ -18,7 +18,7 @@ jobs: strategy: matrix: php-versions: [7.4, 8.0, 8.1] - databases: [testing, pgsql, mysql, mariadb] + databases: [testing, pgsql, mysql] caches: [array, redis, memcached] services: @@ -70,19 +70,6 @@ jobs: ports: - 3306:3306 - mariadb: - image: mariadb:10.5 - env: - MYSQL_ROOT_PASSWORD: wallet - MYSQL_DATABASE: wallet - options: >- - --health-cmd="mysqladmin ping" - --health-interval 10s - --health-timeout 5s - --health-retries 3 - ports: - - 3307:3306 - steps: - name: Checkout uses: actions/checkout@v2 diff --git a/config/config.php b/config/config.php index db9abdd37..13c24f476 100644 --- a/config/config.php +++ b/config/config.php @@ -1,5 +1,7 @@ loadMigrationsFrom([ - dirname(__DIR__).'/migrations', - ]); + $this->loadMigrationsFrom([dirname(__DIR__).'/migrations']); } } diff --git a/tests/TableTest.php b/tests/TableTest.php new file mode 100644 index 000000000..476537b1c --- /dev/null +++ b/tests/TableTest.php @@ -0,0 +1,39 @@ +create(); + self::assertSame('wallet', $user->wallet->getTable()); + } + + public function testTransactionTableName(): void + { + /** @var User $user */ + $user = UserFactory::new()->create(); + $transaction = $user->deposit(100); + self::assertSame('transaction', $transaction->getTable()); + } + + public function testTransferTableName(): void + { + /** + * @var User $user1 + * @var User $user2 + */ + [$user1, $user2] = UserFactory::times(2)->create(); + $user1->deposit(1000); + $transfer = $user1->transfer($user2, 1000); + self::assertSame('transfer', $transfer->getTable()); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index ed632a09f..7a17dab2a 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -19,11 +19,6 @@ class TestCase extends OrchestraTestCase { use RefreshDatabase; - public function setUp(): void - { - parent::setUp(); - } - public function expectExceptionMessageStrict(string $message): void { $this->expectExceptionMessageMatches("~^{$message}$~"); @@ -31,44 +26,38 @@ public function expectExceptionMessageStrict(string $message): void /** * @param Application $app + * + * @return non-empty-array */ protected function getPackageProviders($app): array { - $this->updateConfig($app); + // Bind eloquent models to IoC container + $app['config']->set('wallet.services.exchange', MyExchange::class); + $app['config']->set('wallet.transaction.model', Transaction::class); + $app['config']->set('wallet.transfer.model', Transfer::class); + $app['config']->set('wallet.wallet.model', Wallet::class); return [WalletServiceProvider::class]; } - protected function updateConfig(Application $app): void + /** + * @param Application $app + */ + protected function getEnvironmentSetUp($app): void { /** @var Repository $config */ $config = $app['config']; - // Bind eloquent models to IoC container - $config->set('wallet.services.exchange', MyExchange::class); - // database $config->set('database.connections.testing.prefix', 'tests'); $config->set('database.connections.pgsql.prefix', 'tests'); $config->set('database.connections.mysql.prefix', 'tests'); - $mysql = $config->get('database.connections.mysql'); - $mariadb = array_merge($mysql, ['port' => 3307]); - $percona = array_merge($mysql, ['port' => 3308]); - - $config->set('database.connections.mariadb', $mariadb); - $config->set('database.connections.percona', $percona); - // new table name's $config->set('wallet.transaction.table', 'transaction'); $config->set('wallet.transfer.table', 'transfer'); $config->set('wallet.wallet.table', 'wallet'); - // override model's - $config->set('wallet.transaction.model', Transaction::class); - $config->set('wallet.transfer.model', Transfer::class); - $config->set('wallet.wallet.model', Wallet::class); - $config->set('wallet.cache.driver', $config->get('cache.driver')); $config->set('wallet.lock.driver', $config->get('cache.driver')); } From aa96afc596b0694e809d21508338143ef1ea51d8 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 11 Nov 2021 09:42:37 +0300 Subject: [PATCH 46/51] return mariadb in phpunits.yaml --- .github/workflows/phpunits.yaml | 21 +++++++++++++++++++-- tests/TestCase.php | 3 +++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/workflows/phpunits.yaml b/.github/workflows/phpunits.yaml index 534bd99da..227a44ecf 100644 --- a/.github/workflows/phpunits.yaml +++ b/.github/workflows/phpunits.yaml @@ -18,7 +18,7 @@ jobs: strategy: matrix: php-versions: [7.4, 8.0, 8.1] - databases: [testing, pgsql, mysql] + databases: [testing, pgsql, mysql, mariadb] caches: [array, redis, memcached] services: @@ -70,6 +70,19 @@ jobs: ports: - 3306:3306 + mariadb: + image: mariadb:10.5 + env: + MYSQL_ROOT_PASSWORD: wallet + MYSQL_DATABASE: wallet + options: >- + --health-cmd="mysqladmin ping" + --health-interval 10s + --health-timeout 5s + --health-retries 3 + ports: + - 3307:3306 + steps: - name: Checkout uses: actions/checkout@v2 @@ -109,7 +122,11 @@ jobs: - name: Run test suite run: | - composer parabench + if [[ $DB_CONNECTION == "mariadb" ]]; then + composer unit + else + composer parabench + fi env: CACHE_DRIVER: ${{ matrix.caches }} DB_CONNECTION: ${{ matrix.databases }} diff --git a/tests/TestCase.php b/tests/TestCase.php index 7a17dab2a..951ae6364 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -53,6 +53,9 @@ protected function getEnvironmentSetUp($app): void $config->set('database.connections.pgsql.prefix', 'tests'); $config->set('database.connections.mysql.prefix', 'tests'); + $mysql = $config->get('database.connections.mysql'); + $config->set('database.connections.mariadb', array_merge($mysql, ['port' => 3307])); + // new table name's $config->set('wallet.transaction.table', 'transaction'); $config->set('wallet.transfer.table', 'transfer'); From 659a7617d725a7497e1a2ea5fabae2c662eb3dbf Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 11 Nov 2021 12:16:38 +0300 Subject: [PATCH 47/51] transfer optimize --- src/Internal/ConsistencyInterface.php | 9 +++ src/Internal/Dto/TransferLazyDto.php | 12 ++- src/Internal/Service/PrepareService.php | 8 +- src/Services/CommonService.php | 102 +++++++++++------------- src/Services/ConsistencyService.php | 39 +++++++-- src/Traits/CanExchange.php | 4 +- src/Traits/CartPay.php | 37 ++++----- src/Traits/HasGift.php | 4 +- src/Traits/HasWallet.php | 4 +- tests/DiscountTaxTest.php | 2 +- 10 files changed, 128 insertions(+), 93 deletions(-) diff --git a/src/Internal/ConsistencyInterface.php b/src/Internal/ConsistencyInterface.php index a10110314..5b95f7270 100644 --- a/src/Internal/ConsistencyInterface.php +++ b/src/Internal/ConsistencyInterface.php @@ -8,6 +8,7 @@ use Bavix\Wallet\Exceptions\BalanceIsEmpty; use Bavix\Wallet\Exceptions\InsufficientFunds; use Bavix\Wallet\Interfaces\Wallet; +use Bavix\Wallet\Internal\Dto\TransferLazyDto; interface ConsistencyInterface { @@ -25,4 +26,12 @@ public function checkPositive($amount): void; * @throws InsufficientFunds */ public function checkPotential(Wallet $object, $amount, bool $allowZero = false): void; + + /** + * @param TransferLazyDto[] $objects + * + * @throws BalanceIsEmpty + * @throws InsufficientFunds + */ + public function checkTransfer(array $objects): void; } diff --git a/src/Internal/Dto/TransferLazyDto.php b/src/Internal/Dto/TransferLazyDto.php index fe0811e47..8116e6163 100644 --- a/src/Internal/Dto/TransferLazyDto.php +++ b/src/Internal/Dto/TransferLazyDto.php @@ -15,13 +15,16 @@ class TransferLazyDto private TransactionDto $depositDto; private TransactionDto $withdrawDto; + private string $status; + public function __construct( Wallet $fromWallet, Wallet $toWallet, int $discount, string $fee, TransactionDto $withdrawDto, - TransactionDto $depositDto + TransactionDto $depositDto, + string $status ) { $this->fromWallet = $fromWallet; $this->toWallet = $toWallet; @@ -30,6 +33,8 @@ public function __construct( $this->withdrawDto = $withdrawDto; $this->depositDto = $depositDto; + + $this->status = $status; } public function getFromWallet(): Wallet @@ -61,4 +66,9 @@ public function getDepositDto(): TransactionDto { return $this->depositDto; } + + public function getStatus(): string + { + return $this->status; + } } diff --git a/src/Internal/Service/PrepareService.php b/src/Internal/Service/PrepareService.php index 18ce0b771..363639b3b 100644 --- a/src/Internal/Service/PrepareService.php +++ b/src/Internal/Service/PrepareService.php @@ -67,14 +67,15 @@ public function withdraw(Wallet $wallet, string $amount, ?array $meta, bool $con ); } - public function transferLazy(Wallet $from, Wallet $to, $amount, ?array $meta = null): TransferLazyDto + public function transferLazy(Wallet $from, Wallet $to, string $status, $amount, ?array $meta = null): TransferLazyDto { $discount = $this->walletService->discount($from, $to); $from = $this->walletService->getWallet($from); - $fee = (string) $this->walletService->fee($to, $amount); // replace max => mathService.max $depositAmount = (string) max(0, $this->mathService->sub($amount, $discount)); + $fee = (string) $this->walletService->fee($to, $depositAmount); + $withdrawAmount = $this->mathService->add($depositAmount, $fee, $from->decimal_places); return new TransferLazyDto( @@ -83,7 +84,8 @@ public function transferLazy(Wallet $from, Wallet $to, $amount, ?array $meta = n $discount, $fee, $this->withdraw($from, $withdrawAmount, $meta), - $this->deposit($to, $depositAmount, $meta) + $this->deposit($to, $depositAmount, $meta), + $status ); } } diff --git a/src/Services/CommonService.php b/src/Services/CommonService.php index 3e1f216b4..1efdabfd8 100644 --- a/src/Services/CommonService.php +++ b/src/Services/CommonService.php @@ -3,14 +3,11 @@ namespace Bavix\Wallet\Services; use Bavix\Wallet\Exceptions\AmountInvalid; -use Bavix\Wallet\Exceptions\BalanceIsEmpty; -use Bavix\Wallet\Exceptions\InsufficientFunds; use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Internal\Assembler\TransferDtoAssembler; use Bavix\Wallet\Internal\BookkeeperInterface; -use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\Dto\TransactionDto; -use Bavix\Wallet\Internal\MathInterface; +use Bavix\Wallet\Internal\Dto\TransferLazyDto; use Bavix\Wallet\Internal\Service\AssistantService; use Bavix\Wallet\Internal\Service\AtmService; use Bavix\Wallet\Internal\Service\CastService; @@ -19,31 +16,26 @@ use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Models\Wallet as WalletModel; use function compact; -use function max; use Throwable; class CommonService { private DbService $dbService; private LockService $lockService; - private MathInterface $math; private AtmService $atmService; private CastService $castService; private WalletService $walletService; private AssistantService $assistantService; private PrepareService $prepareService; private BookkeeperInterface $bookkeeper; - private ConsistencyInterface $consistency; private TransferDtoAssembler $transferDtoAssembler; public function __construct( DbService $dbService, LockService $lockService, - MathInterface $math, CastService $castService, WalletService $walletService, BookkeeperInterface $bookkeeper, - ConsistencyInterface $consistency, AssistantService $satisfyService, PrepareService $prepareService, TransferDtoAssembler $transferDtoAssembler, @@ -51,12 +43,10 @@ public function __construct( ) { $this->dbService = $dbService; $this->lockService = $lockService; - $this->math = $math; $this->atmService = $atmService; $this->castService = $castService; $this->walletService = $walletService; $this->bookkeeper = $bookkeeper; - $this->consistency = $consistency; $this->assistantService = $satisfyService; $this->prepareService = $prepareService; $this->transferDtoAssembler = $transferDtoAssembler; @@ -66,57 +56,59 @@ public function __construct( * @param int|string $amount * * @throws AmountInvalid - * @throws BalanceIsEmpty - * @throws InsufficientFunds * @throws Throwable */ - public function transfer(Wallet $from, Wallet $to, $amount, ?array $meta = null, string $status = Transfer::STATUS_TRANSFER): Transfer + public function forceTransfer(Wallet $from, Wallet $to, $amount, ?array $meta = null, string $status = Transfer::STATUS_TRANSFER): Transfer { - $discount = $this->walletService->discount($from, $to); - $newAmount = max(0, $this->math->sub($amount, $discount)); - $fee = $this->walletService->fee($to, $newAmount); - $this->consistency->checkPotential($from, $this->math->add($newAmount, $fee)); + $transferLazyDto = $this->prepareService->transferLazy($from, $to, $status, $amount, $meta); + $transfers = $this->applyTransfers([$transferLazyDto]); - return $this->forceTransfer($from, $to, $amount, $meta, $status); + return current($transfers); } /** - * @param int|string $amount + * @param TransferLazyDto[] $objects * - * @throws AmountInvalid - * @throws Throwable + * @return Transfer[] */ - public function forceTransfer(Wallet $from, Wallet $to, $amount, ?array $meta = null, string $status = Transfer::STATUS_TRANSFER): Transfer + public function applyTransfers(array $objects): array { - return $this->dbService->transaction(function () use ($from, $to, $amount, $meta, $status) { - $transferLazyDto = $this->prepareService->transferLazy($from, $to, $amount, $meta); - $withdrawDto = $transferLazyDto->getWithdrawDto(); - $depositDto = $transferLazyDto->getDepositDto(); - - $transactions = $this->applyOperations( - [$withdrawDto->getWalletId() => $from, $depositDto->getWalletId() => $to], - [$withdrawDto, $depositDto], - ); - - $withdraw = $transactions[$withdrawDto->getUuid()] ?? null; - assert($withdraw !== null); - - $deposit = $transactions[$depositDto->getUuid()] ?? null; - assert($deposit !== null); - - $transfer = $this->transferDtoAssembler->create( - $deposit->getKey(), - $withdraw->getKey(), - $status, - $this->castService->getModel($transferLazyDto->getFromWallet()), - $this->castService->getModel($transferLazyDto->getToWallet()), - $transferLazyDto->getDiscount(), - $transferLazyDto->getFee() - ); - - $transfers = $this->atmService->makeTransfers([$transfer]); - - return current($transfers); + return $this->dbService->transaction(function () use ($objects): array { + $wallets = []; + $operations = []; + foreach ($objects as $object) { + $fromWallet = $this->castService->getWallet($object->getFromWallet()); + $wallets[$fromWallet->getKey()] = $fromWallet; + + $toWallet = $this->castService->getWallet($object->getToWallet()); + $wallets[$toWallet->getKey()] = $toWallet; + + $operations[] = $object->getWithdrawDto(); + $operations[] = $object->getDepositDto(); + } + + $transactions = $this->applyTransactions($wallets, $operations); + + $transfers = []; + foreach ($objects as $object) { + $withdraw = $transactions[$object->getWithdrawDto()->getUuid()] ?? null; + assert($withdraw !== null); + + $deposit = $transactions[$object->getDepositDto()->getUuid()] ?? null; + assert($deposit !== null); + + $transfers[] = $this->transferDtoAssembler->create( + $deposit->getKey(), + $withdraw->getKey(), + $object->getStatus(), + $this->castService->getModel($object->getFromWallet()), + $this->castService->getModel($object->getToWallet()), + $object->getDiscount(), + $object->getFee() + ); + } + + return $this->atmService->makeTransfers($transfers); }); } @@ -153,7 +145,7 @@ public function addBalance(Wallet $wallet, $amount): bool /** * @param float|int|string $amount */ - public function makeOperation(Wallet $wallet, string $type, $amount, ?array $meta, bool $confirmed = true): Transaction + public function makeTransaction(Wallet $wallet, string $type, $amount, ?array $meta, bool $confirmed = true): Transaction { assert(in_array($type, [Transaction::TYPE_DEPOSIT, Transaction::TYPE_WITHDRAW], true)); @@ -163,7 +155,7 @@ public function makeOperation(Wallet $wallet, string $type, $amount, ?array $met $dto = $this->prepareService->withdraw($wallet, (string) $amount, $meta, $confirmed); } - $transactions = $this->applyOperations( + $transactions = $this->applyTransactions( [$dto->getWalletId() => $wallet], [$dto], ); @@ -177,7 +169,7 @@ public function makeOperation(Wallet $wallet, string $type, $amount, ?array $met * * @return non-empty-array */ - public function applyOperations(array $wallets, array $objects): array + public function applyTransactions(array $wallets, array $objects): array { $transactions = $this->atmService->makeTransactions($objects); // q1 $totals = $this->assistantService->getSums($objects); diff --git a/src/Services/ConsistencyService.php b/src/Services/ConsistencyService.php index 76b1f4931..4e310c02a 100644 --- a/src/Services/ConsistencyService.php +++ b/src/Services/ConsistencyService.php @@ -9,20 +9,23 @@ use Bavix\Wallet\Exceptions\InsufficientFunds; use Bavix\Wallet\Interfaces\Wallet; use Bavix\Wallet\Internal\ConsistencyInterface; +use Bavix\Wallet\Internal\Dto\TransferLazyDto; use Bavix\Wallet\Internal\MathInterface; use Bavix\Wallet\Internal\Service\CastService; class ConsistencyService implements ConsistencyInterface { private CastService $castService; - - private MathInterface $math; + private MathInterface $mathService; + private WalletService $walletService; public function __construct( - MathInterface $math, + WalletService $walletService, + MathInterface $mathService, CastService $castService ) { - $this->math = $math; + $this->walletService = $walletService; + $this->mathService = $mathService; $this->castService = $castService; } @@ -33,7 +36,7 @@ public function __construct( */ public function checkPositive($amount): void { - if ($this->math->compare($amount, 0) === -1) { + if ($this->mathService->compare($amount, 0) === -1) { throw new AmountInvalid(trans('wallet::errors.price_positive')); } } @@ -56,4 +59,30 @@ public function checkPotential(Wallet $object, $amount, bool $allowZero = false) throw new InsufficientFunds(trans('wallet::errors.insufficient_funds')); } } + + /** + * @param TransferLazyDto[] $objects + * + * @throws BalanceIsEmpty + * @throws InsufficientFunds + */ + public function checkTransfer(array $objects): void + { + $wallets = []; + $totalAmount = []; + foreach ($objects as $object) { + $withdrawDto = $object->getWithdrawDto(); + $wallet = $this->castService->getWallet($object->getFromWallet(), false); + $wallets[] = $wallet; + + $totalAmount[$wallet->uuid] = $this->mathService->add( + ($totalAmount[$wallet->uuid] ?? 0), + $this->mathService->negative($withdrawDto->getAmount()) + ); + } + + foreach ($wallets as $wallet) { + $this->checkPotential($wallet, $totalAmount[$wallet->uuid] ?? -1); + } + } } diff --git a/src/Traits/CanExchange.php b/src/Traits/CanExchange.php index 7de0d662a..04f579d38 100644 --- a/src/Traits/CanExchange.php +++ b/src/Traits/CanExchange.php @@ -62,11 +62,11 @@ public function forceExchange(Wallet $to, $amount, ?array $meta = null): Transfe ); $withdraw = app(CommonService::class) - ->makeOperation($from, Transaction::TYPE_WITHDRAW, $math->add($amount, $fee), $meta) + ->makeTransaction($from, Transaction::TYPE_WITHDRAW, $math->add($amount, $fee), $meta) ; $deposit = app(CommonService::class) - ->makeOperation($to, Transaction::TYPE_DEPOSIT, $math->floor($math->mul($amount, $rate, 1)), $meta) + ->makeTransaction($to, Transaction::TYPE_DEPOSIT, $math->floor($math->mul($amount, $rate, 1)), $meta) ; $castService = app(CastService::class); diff --git a/src/Traits/CartPay.php b/src/Traits/CartPay.php index 1978d14be..6085d0e63 100644 --- a/src/Traits/CartPay.php +++ b/src/Traits/CartPay.php @@ -10,6 +10,7 @@ use Bavix\Wallet\Internal\ConsistencyInterface; use Bavix\Wallet\Internal\Dto\AvailabilityDto; use Bavix\Wallet\Internal\PurchaseInterface; +use Bavix\Wallet\Internal\Service\PrepareService; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Objects\Cart; use Bavix\Wallet\Services\CommonService; @@ -75,33 +76,25 @@ public function payCart(CartInterface $cart, bool $force = false): array throw new ProductEnded(trans('wallet::errors.product_stock')); } - $self = $this; - - return app(DbService::class)->transaction(static function () use ($self, $cart, $force) { - $results = []; + return app(DbService::class)->transaction(function () use ($cart, $force) { + $transfers = []; + $prepareService = app(PrepareService::class); + $metaService = app(MetaService::class); foreach ($cart->getBasketDto()->cursor() as $product) { - if ($force) { - $results[] = app(CommonService::class)->forceTransfer( - $self, - $product, - $product->getAmountProduct($self), - app(MetaService::class)->getMeta($cart, $product), - Transfer::STATUS_PAID - ); - - continue; - } - - $results[] = app(CommonService::class)->transfer( - $self, + $transfers[] = $prepareService->transferLazy( + $this, $product, - $product->getAmountProduct($self), - app(MetaService::class)->getMeta($cart, $product), - Transfer::STATUS_PAID + Transfer::STATUS_PAID, + $product->getAmountProduct($this), + $metaService->getMeta($cart, $product) ); } - return $results; + if ($force === false) { + app(ConsistencyInterface::class)->checkTransfer($transfers); + } + + return app(CommonService::class)->applyTransfers($transfers); }); } diff --git a/src/Traits/HasGift.php b/src/Traits/HasGift.php index 793546523..7a7f36a5f 100644 --- a/src/Traits/HasGift.php +++ b/src/Traits/HasGift.php @@ -82,8 +82,8 @@ public function gift(Wallet $to, Product $product, bool $force = false): Transfe app(ConsistencyInterface::class)->checkPotential($santa, $math->add($amount, $fee)); } - $withdraw = $commonService->makeOperation($santa, Transaction::TYPE_WITHDRAW, $math->add($amount, $fee), $meta); - $deposit = $commonService->makeOperation($product, Transaction::TYPE_DEPOSIT, $amount, $meta); + $withdraw = $commonService->makeTransaction($santa, Transaction::TYPE_WITHDRAW, $math->add($amount, $fee), $meta); + $deposit = $commonService->makeTransaction($product, Transaction::TYPE_DEPOSIT, $amount, $meta); $from = app(WalletService::class) ->getWallet($to) diff --git a/src/Traits/HasWallet.php b/src/Traits/HasWallet.php index fdded40a7..6523d80ce 100644 --- a/src/Traits/HasWallet.php +++ b/src/Traits/HasWallet.php @@ -45,7 +45,7 @@ public function deposit($amount, ?array $meta = null, bool $confirmed = true): T { return app(DbService::class)->transaction(function () use ($amount, $meta, $confirmed) { return app(CommonService::class) - ->makeOperation($this, Transaction::TYPE_DEPOSIT, $amount, $meta, $confirmed) + ->makeTransaction($this, Transaction::TYPE_DEPOSIT, $amount, $meta, $confirmed) ; }); } @@ -186,7 +186,7 @@ public function forceWithdraw($amount, ?array $meta = null, bool $confirmed = tr return app(DbService::class)->transaction(static function () use ($self, $amount, $meta, $confirmed) { return app(CommonService::class) - ->makeOperation($self, Transaction::TYPE_WITHDRAW, $amount, $meta, $confirmed) + ->makeTransaction($self, Transaction::TYPE_WITHDRAW, $amount, $meta, $confirmed) ; }); } diff --git a/tests/DiscountTaxTest.php b/tests/DiscountTaxTest.php index 74cd3d698..34cbb46e5 100644 --- a/tests/DiscountTaxTest.php +++ b/tests/DiscountTaxTest.php @@ -74,7 +74,7 @@ public function testPay(): void self::assertEquals($product->getKey(), $transfer->to->getKey()); } - public function testRefund(): void + public function testRefundPersonalDiscountAndTax(): void { /** * @var Buyer $buyer From 336f8e0f3cb7afa3f8153d190fc5ce990c2f9b1b Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 11 Nov 2021 12:26:05 +0300 Subject: [PATCH 48/51] update fee --- src/Internal/Service/PrepareService.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Internal/Service/PrepareService.php b/src/Internal/Service/PrepareService.php index 363639b3b..434be167b 100644 --- a/src/Internal/Service/PrepareService.php +++ b/src/Internal/Service/PrepareService.php @@ -71,10 +71,10 @@ public function transferLazy(Wallet $from, Wallet $to, string $status, $amount, { $discount = $this->walletService->discount($from, $to); $from = $this->walletService->getWallet($from); + $fee = (string) $this->walletService->fee($to, $amount); // replace max => mathService.max $depositAmount = (string) max(0, $this->mathService->sub($amount, $discount)); - $fee = (string) $this->walletService->fee($to, $depositAmount); $withdrawAmount = $this->mathService->add($depositAmount, $fee, $from->decimal_places); From 50562e4b231d09f78c7777eb29dedd13d1e23ac4 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 11 Nov 2021 13:16:45 +0300 Subject: [PATCH 49/51] optimize refundCart --- src/Services/ConsistencyService.php | 2 +- src/Traits/CartPay.php | 48 ++++++++++++++++------------- src/Traits/HasWallet.php | 2 -- tests/DiscountTaxTest.php | 2 +- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/Services/ConsistencyService.php b/src/Services/ConsistencyService.php index 4e310c02a..ad3c9520a 100644 --- a/src/Services/ConsistencyService.php +++ b/src/Services/ConsistencyService.php @@ -51,7 +51,7 @@ public function checkPotential(Wallet $object, $amount, bool $allowZero = false) { $wallet = $this->castService->getWallet($object, false); - if ($amount && !$wallet->balance) { + if (($this->mathService->compare($amount, 0) !== 0) && !$wallet->getBalanceAttribute()) { throw new BalanceIsEmpty(trans('wallet::errors.wallet_empty')); } diff --git a/src/Traits/CartPay.php b/src/Traits/CartPay.php index 6085d0e63..c7c86392f 100644 --- a/src/Traits/CartPay.php +++ b/src/Traits/CartPay.php @@ -36,21 +36,21 @@ public function payFreeCart(CartInterface $cart): array app(ConsistencyInterface::class)->checkPotential($this, 0, true); - $self = $this; - - return app(DbService::class)->transaction(static function () use ($self, $cart) { - $results = []; + return app(DbService::class)->transaction(function () use ($cart) { + $transfers = []; + $prepareService = app(PrepareService::class); + $metaService = app(MetaService::class); foreach ($cart->getBasketDto()->cursor() as $product) { - $results[] = app(CommonService::class)->forceTransfer( - $self, + $transfers[] = $prepareService->transferLazy( + $this, $product, + Transfer::STATUS_PAID, 0, - app(MetaService::class)->getMeta($cart, $product), - Transfer::STATUS_PAID + $metaService->getMeta($cart, $product) ); } - return $results; + return app(CommonService::class)->applyTransfers($transfers); }); } @@ -117,17 +117,17 @@ public function safeRefundCart(CartInterface $cart, bool $force = false, bool $g public function refundCart(CartInterface $cart, bool $force = false, bool $gifts = false): bool { - $self = $this; - - return app(DbService::class)->transaction(static function () use ($self, $cart, $force, $gifts) { + return app(DbService::class)->transaction(function () use ($cart, $force, $gifts) { $results = []; - $transfers = app(PurchaseInterface::class)->already($self, $cart->getBasketDto(), $gifts); + $transfers = app(PurchaseInterface::class)->already($this, $cart->getBasketDto(), $gifts); if (count($transfers) !== $cart->getBasketDto()->total()) { throw (new ModelNotFoundException()) - ->setModel($self->transfers()->getMorphClass()) + ->setModel($this->transfers()->getMorphClass()) ; } + $objects = []; + $prepareService = app(PrepareService::class); foreach ($cart->getBasketDto()->cursor() as $product) { $transfer = current($transfers); next($transfers); @@ -135,19 +135,25 @@ public function refundCart(CartInterface $cart, bool $force = false, bool $gifts * the code is extremely poorly written, a complete refactoring is required. * for version 6.x we will leave it as it is. */ - $transfer->load('withdraw.wallet'); + $transfer->load('withdraw.wallet'); // fixme: need optimize - if (!$force) { - app(ConsistencyInterface::class)->checkPotential($product, $transfer->deposit->amount); - } - - app(CommonService::class)->forceTransfer( + $objects[] = $prepareService->transferLazy( $product, $transfer->withdraw->wallet, - $transfer->deposit->amount, + Transfer::STATUS_TRANSFER, + $transfer->deposit->amount, // fixme: need optimize app(MetaService::class)->getMeta($cart, $product) ); + } + + if ($force === false) { + app(ConsistencyInterface::class)->checkTransfer($objects); + } + + app(CommonService::class)->applyTransfers($objects); + // fixme: one query update for + foreach ($transfers as $transfer) { $results[] = $transfer->update([ 'status' => Transfer::STATUS_REFUND, 'status_last' => $transfer->status, diff --git a/src/Traits/HasWallet.php b/src/Traits/HasWallet.php index 6523d80ce..abe2788db 100644 --- a/src/Traits/HasWallet.php +++ b/src/Traits/HasWallet.php @@ -71,8 +71,6 @@ public function deposit($amount, ?array $meta = null, bool $confirmed = true): T * $user2->deposit(100); * var_dump($user2->balance); // 300 * - * @throws Throwable - * * @return float|int|string */ public function getBalanceAttribute() diff --git a/tests/DiscountTaxTest.php b/tests/DiscountTaxTest.php index 34cbb46e5..968ea41d1 100644 --- a/tests/DiscountTaxTest.php +++ b/tests/DiscountTaxTest.php @@ -237,7 +237,7 @@ public function testForcePay(): void self::assertEquals($buyer->balance, 0); } - public function testPayFree(): void + public function testPayFreeAndRefund(): void { /** * @var Buyer $buyer From ba1fca36fd5e99f465a63da9e131fac6733bcdba Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 11 Nov 2021 13:31:09 +0300 Subject: [PATCH 50/51] optimize exchange --- src/Traits/CanExchange.php | 50 +++++++++++++++----------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/src/Traits/CanExchange.php b/src/Traits/CanExchange.php index 04f579d38..2dfafe7e2 100644 --- a/src/Traits/CanExchange.php +++ b/src/Traits/CanExchange.php @@ -3,13 +3,12 @@ namespace Bavix\Wallet\Traits; use Bavix\Wallet\Interfaces\Wallet; -use Bavix\Wallet\Internal\Assembler\TransferDtoAssembler; use Bavix\Wallet\Internal\ConsistencyInterface; +use Bavix\Wallet\Internal\Dto\TransferLazyDto; use Bavix\Wallet\Internal\ExchangeInterface; use Bavix\Wallet\Internal\MathInterface; -use Bavix\Wallet\Internal\Service\AtmService; use Bavix\Wallet\Internal\Service\CastService; -use Bavix\Wallet\Models\Transaction; +use Bavix\Wallet\Internal\Service\PrepareService; use Bavix\Wallet\Models\Transfer; use Bavix\Wallet\Services\CommonService; use Bavix\Wallet\Services\DbService; @@ -47,41 +46,32 @@ public function safeExchange(Wallet $to, $amount, ?array $meta = null): ?Transfe */ public function forceExchange(Wallet $to, $amount, ?array $meta = null): Transfer { - /** @var Wallet $from */ - $from = app(WalletService::class)->getWallet($this); - - return app(LockService::class)->lock($this, __FUNCTION__, static function () use ($from, $to, $amount, $meta) { - return app(DbService::class)->transaction(static function () use ($from, $to, $amount, $meta) { - $walletService = app(WalletService::class); - $math = app(MathInterface::class); - $fee = $walletService->fee($to, $amount); + return app(LockService::class)->lock($this, __FUNCTION__, function () use ($to, $amount, $meta) { + return app(DbService::class)->transaction(function () use ($to, $amount, $meta) { + $prepareService = app(PrepareService::class); + $mathService = app(MathInterface::class); + $castService = app(CastService::class); + $fee = app(WalletService::class)->fee($to, $amount); $rate = app(ExchangeInterface::class)->convertTo( - $walletService->getWallet($from)->currency, - $walletService->getWallet($to)->currency, + $castService->getWallet($this)->currency, + $castService->getWallet($to)->currency, 1 ); - $withdraw = app(CommonService::class) - ->makeTransaction($from, Transaction::TYPE_WITHDRAW, $math->add($amount, $fee), $meta) - ; + $withdrawDto = $prepareService->withdraw($this, $mathService->add($amount, $fee), $meta); + $depositDto = $prepareService->deposit($to, $mathService->floor($mathService->mul($amount, $rate, 1)), $meta); - $deposit = app(CommonService::class) - ->makeTransaction($to, Transaction::TYPE_DEPOSIT, $math->floor($math->mul($amount, $rate, 1)), $meta) - ; - - $castService = app(CastService::class); - - $transfer = app(TransferDtoAssembler::class)->create( - $deposit->getKey(), - $withdraw->getKey(), - Transfer::STATUS_EXCHANGE, - $castService->getModel($from), - $castService->getModel($to), + $transferLazyDto = new TransferLazyDto( + $this, + $to, 0, - $fee + $fee, + $withdrawDto, + $depositDto, + Transfer::STATUS_EXCHANGE, ); - $transfers = app(AtmService::class)->makeTransfers([$transfer]); + $transfers = app(CommonService::class)->applyTransfers([$transferLazyDto]); return current($transfers); }); From 31558a72e2452685477ecd3a770efebf28329773 Mon Sep 17 00:00:00 2001 From: Maxim Babichev Date: Thu, 11 Nov 2021 13:41:51 +0300 Subject: [PATCH 51/51] drop LockService::bindTo --- src/Services/LockService.php | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/Services/LockService.php b/src/Services/LockService.php index 7a8a9559e..2616f73ea 100644 --- a/src/Services/LockService.php +++ b/src/Services/LockService.php @@ -8,8 +8,6 @@ use Closure; use function get_class; use Illuminate\Database\Eloquent\Model; -use ReflectionException; -use ReflectionFunction; /** * @deprecated @@ -36,20 +34,7 @@ public function lock(object $self, string $name, Closure $closure) return $this->atomicService->block( "legacy_{$name}.{$uniqId}", - $this->bindTo($self, $closure) + $closure->bindTo($self) ); } - - /** - * @throws ReflectionException - */ - protected function bindTo(object $self, Closure $closure): Closure - { - $reflect = new ReflectionFunction($closure); - if (strpos((string) $reflect, 'static') === false) { - return $closure->bindTo($self); - } - - return $closure; - } }