Skip to content

Commit 477ba8d

Browse files
committed
[7.1] Transaction State
1 parent 4a0cea9 commit 477ba8d

17 files changed

+295
-34
lines changed

config/config.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use Bavix\Wallet\Services\ExchangeService;
3434
use Bavix\Wallet\Services\PrepareService;
3535
use Bavix\Wallet\Services\PurchaseService;
36+
use Bavix\Wallet\Services\RegulatorService;
3637
use Bavix\Wallet\Services\TaxService;
3738

3839
return [
@@ -78,6 +79,7 @@
7879
'atomic' => AtomicService::class,
7980
'basket' => BasketService::class,
8081
'bookkeeper' => BookkeeperService::class,
82+
'regulator' => RegulatorService::class,
8183
'cast' => CastService::class,
8284
'consistency' => ConsistencyService::class,
8385
'discount' => DiscountService::class,

phpstan-baseline.neon

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
parameters:
2+
ignoreErrors:
3+
-
4+
message: "#^Call to an undefined method Illuminate\\\\Contracts\\\\Foundation\\\\Application\\:\\:makeWith\\(\\)\\.$#"
5+
count: 2
6+
path: src/WalletServiceProvider.php
7+

phpstan.neon

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
includes:
2+
- phpstan-baseline.neon
3+
14
parameters:
25
checkMissingIterableValueType: false
36
fileExtensions:

src/Internal/Service/DatabaseService.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use Bavix\Wallet\Internal\Exceptions\ExceptionInterface;
88
use Bavix\Wallet\Internal\Exceptions\TransactionFailedException;
9-
use Closure;
9+
use Bavix\Wallet\Services\StateServiceInterface;
1010
use Illuminate\Config\Repository as ConfigRepository;
1111
use Illuminate\Database\ConnectionInterface;
1212
use Illuminate\Database\ConnectionResolverInterface;
@@ -15,12 +15,15 @@
1515

1616
final class DatabaseService implements DatabaseServiceInterface
1717
{
18+
private StateServiceInterface $stateService;
1819
private ConnectionInterface $connection;
1920

2021
public function __construct(
22+
StateServiceInterface $stateService,
2123
ConnectionResolverInterface $connectionResolver,
2224
ConfigRepository $config
2325
) {
26+
$this->stateService = $stateService;
2427
$this->connection = $connectionResolver->connection(
2528
$config->get('wallet.database.connection')
2629
);
@@ -40,10 +43,19 @@ public function transaction(callable $callback)
4043
return $callback();
4144
}
4245

43-
return $this->connection->transaction(Closure::fromCallable($callback));
46+
return $this->connection->transaction(function () use ($callback) {
47+
$result = $callback();
48+
$this->stateService->commit();
49+
50+
return $result;
51+
});
4452
} catch (RecordsNotFoundException|ExceptionInterface $exception) {
53+
$this->stateService->purge();
54+
4555
throw $exception;
4656
} catch (Throwable $throwable) {
57+
$this->stateService->purge();
58+
4759
throw new TransactionFailedException(
4860
'Transaction failed',
4961
ExceptionInterface::TRANSACTION_FAILED,

src/Internal/Service/StorageService.php

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,38 @@
77
use Bavix\Wallet\Internal\Exceptions\ExceptionInterface;
88
use Bavix\Wallet\Internal\Exceptions\LockProviderNotFoundException;
99
use Bavix\Wallet\Internal\Exceptions\RecordNotFoundException;
10-
use Illuminate\Cache\CacheManager;
11-
use Illuminate\Config\Repository as ConfigRepository;
1210
use Illuminate\Contracts\Cache\Repository as CacheRepository;
1311

1412
final class StorageService implements StorageServiceInterface
1513
{
1614
private LockServiceInterface $lockService;
1715
private MathServiceInterface $mathService;
18-
private CacheRepository $cache;
16+
private CacheRepository $cacheRepository;
1917

2018
public function __construct(
21-
CacheManager $cacheManager,
22-
ConfigRepository $config,
2319
LockServiceInterface $lockService,
24-
MathServiceInterface $mathService
20+
MathServiceInterface $mathService,
21+
CacheRepository $cacheRepository
2522
) {
23+
$this->cacheRepository = $cacheRepository;
2624
$this->mathService = $mathService;
2725
$this->lockService = $lockService;
28-
$this->cache = $cacheManager->driver(
29-
$config->get('wallet.cache.driver', 'array')
30-
);
3126
}
3227

3328
public function flush(): bool
3429
{
35-
return $this->cache->clear();
30+
return $this->cacheRepository->clear();
3631
}
3732

3833
public function missing(string $key): bool
3934
{
40-
return $this->cache->forget($key);
35+
return $this->cacheRepository->forget($key);
4136
}
4237

4338
/** @throws RecordNotFoundException */
4439
public function get(string $key): string
4540
{
46-
$value = $this->cache->get($key);
41+
$value = $this->cacheRepository->get($key);
4742
if ($value === null) {
4843
throw new RecordNotFoundException(
4944
'The repository did not find the object',
@@ -54,9 +49,10 @@ public function get(string $key): string
5449
return $this->mathService->round($value);
5550
}
5651

52+
/** @param float|int|string $value */
5753
public function sync(string $key, $value): bool
5854
{
59-
return $this->cache->set($key, $value);
55+
return $this->cacheRepository->set($key, $value);
6056
}
6157

6258
/**

src/Services/CastService.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ public function getWallet(Wallet $object, bool $save = true): WalletModel
1919
assert($wallet instanceof WalletModel);
2020
}
2121

22-
if ($save) {
23-
$wallet->exists or $wallet->save();
22+
if ($save && !$wallet->exists) {
23+
$wallet->save();
24+
$wallet->getBalanceAttribute(); // update balance in store
2425
}
2526

2627
return $wallet;

src/Services/CommonServiceLegacy.php

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Bavix\Wallet\Internal\Exceptions\RecordNotFoundException;
1414
use Bavix\Wallet\Internal\Exceptions\TransactionFailedException;
1515
use Bavix\Wallet\Internal\Service\DatabaseServiceInterface;
16+
use Bavix\Wallet\Internal\Service\MathServiceInterface;
1617
use Bavix\Wallet\Models\Transaction;
1718
use Bavix\Wallet\Models\Transfer;
1819
use Bavix\Wallet\Models\Wallet as WalletModel;
@@ -26,25 +27,31 @@ final class CommonServiceLegacy
2627
private DatabaseServiceInterface $databaseService;
2728
private AssistantServiceInterface $assistantService;
2829
private PrepareServiceInterface $prepareService;
29-
private BookkeeperServiceInterface $bookkeeper;
30+
private RegulatorServiceInterface $regulatorService;
3031
private TransferDtoAssemblerInterface $transferDtoAssembler;
32+
private StateServiceInterface $stateService;
33+
private MathServiceInterface $mathService;
3134

3235
public function __construct(
3336
CastServiceInterface $castService,
34-
BookkeeperServiceInterface $bookkeeper,
3537
AssistantServiceInterface $satisfyService,
3638
DatabaseServiceInterface $databaseService,
3739
PrepareServiceInterface $prepareService,
3840
TransferDtoAssemblerInterface $transferDtoAssembler,
39-
AtmServiceInterface $atmService
41+
RegulatorServiceInterface $regulatorService,
42+
StateServiceInterface $stateService,
43+
AtmServiceInterface $atmService,
44+
MathServiceInterface $mathService
4045
) {
4146
$this->atmService = $atmService;
4247
$this->castService = $castService;
43-
$this->bookkeeper = $bookkeeper;
4448
$this->assistantService = $satisfyService;
4549
$this->databaseService = $databaseService;
4650
$this->prepareService = $prepareService;
51+
$this->regulatorService = $regulatorService;
4752
$this->transferDtoAssembler = $transferDtoAssembler;
53+
$this->stateService = $stateService;
54+
$this->mathService = $mathService;
4855
}
4956

5057
/**
@@ -131,7 +138,8 @@ public function addBalance(Wallet $wallet, $amount): bool
131138
return $this->databaseService->transaction(function () use ($wallet, $amount) {
132139
/** @var WalletModel $wallet */
133140
$walletObject = $this->castService->getWallet($wallet);
134-
$balance = $this->bookkeeper->increase($walletObject, $amount);
141+
$balance = $this->regulatorService->increase($walletObject, $amount);
142+
$this->stateService->persist($wallet);
135143
$result = 0;
136144

137145
try {
@@ -143,7 +151,7 @@ public function addBalance(Wallet $wallet, $amount): bool
143151
$walletObject->fill(['balance' => $balance])->syncOriginalAttribute('balance');
144152
} finally {
145153
if ($result === 0) {
146-
$this->bookkeeper->missing($walletObject);
154+
$this->regulatorService->increase($walletObject, $this->mathService->negative($amount));
147155
}
148156
}
149157

@@ -196,7 +204,9 @@ public function applyTransactions(array $wallets, array $objects): array
196204
$object = $this->castService->getWallet($wallet);
197205
assert((int) $object->getKey() === $walletId);
198206

199-
$balance = $this->bookkeeper->increase($object, $total);
207+
$object->getBalanceAttribute();
208+
$balance = $this->regulatorService->increase($object, $total);
209+
$this->stateService->persist($object);
200210

201211
$object->newQuery()->whereKey($object->getKey())->update(['balance' => $balance]); // ?qN
202212
$object->fill(['balance' => $balance])->syncOriginalAttribute('balance');

src/Services/RegulatorService.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Bavix\Wallet\Services;
6+
7+
use Bavix\Wallet\Internal\Exceptions\RecordNotFoundException;
8+
use Bavix\Wallet\Internal\Service\MathServiceInterface;
9+
use Bavix\Wallet\Internal\Service\StorageServiceInterface;
10+
use Bavix\Wallet\Internal\Service\UuidFactoryServiceInterface;
11+
use Bavix\Wallet\Models\Wallet;
12+
13+
final class RegulatorService implements RegulatorServiceInterface
14+
{
15+
private BookkeeperServiceInterface $bookkeeperService;
16+
private StorageServiceInterface $storageService;
17+
private MathServiceInterface $mathService;
18+
private string $idempotentKey;
19+
20+
public function __construct(
21+
UuidFactoryServiceInterface $uuidFactoryService,
22+
BookkeeperServiceInterface $bookkeeperService,
23+
StorageServiceInterface $storageService,
24+
MathServiceInterface $mathService
25+
) {
26+
$this->idempotentKey = $uuidFactoryService->uuid4();
27+
$this->bookkeeperService = $bookkeeperService;
28+
$this->storageService = $storageService;
29+
$this->mathService = $mathService;
30+
}
31+
32+
public function missing(Wallet $wallet): bool
33+
{
34+
return $this->storageService->missing($this->getKey($wallet->uuid));
35+
}
36+
37+
public function diff(Wallet $wallet): string
38+
{
39+
try {
40+
return $this->mathService->round($this->storageService->get($this->getKey($wallet->uuid)));
41+
} catch (RecordNotFoundException $exception) {
42+
return '0';
43+
}
44+
}
45+
46+
public function amount(Wallet $wallet): string
47+
{
48+
return $this->mathService->round(
49+
$this->mathService->add($this->bookkeeperService->amount($wallet), $this->diff($wallet))
50+
);
51+
}
52+
53+
/** @param float|int|string $value */
54+
public function sync(Wallet $wallet, $value): bool
55+
{
56+
return $this->storageService->sync(
57+
$this->getKey($wallet->uuid),
58+
$this->mathService->round(
59+
$this->mathService->negative($this->mathService->sub($this->amount($wallet), $value))
60+
)
61+
);
62+
}
63+
64+
/** @param float|int|string $value */
65+
public function increase(Wallet $wallet, $value): string
66+
{
67+
try {
68+
$this->storageService->increase($this->getKey($wallet->uuid), $value);
69+
} catch (RecordNotFoundException $exception) {
70+
$value = $this->mathService->round($value);
71+
$this->storageService->sync($this->getKey($wallet->uuid), $value);
72+
} finally {
73+
return $this->amount($wallet);
74+
}
75+
}
76+
77+
private function getKey(string $uuid): string
78+
{
79+
return $this->idempotentKey.'::'.$uuid;
80+
}
81+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Bavix\Wallet\Services;
6+
7+
use Bavix\Wallet\Models\Wallet;
8+
9+
interface RegulatorServiceInterface
10+
{
11+
public function missing(Wallet $wallet): bool;
12+
13+
public function diff(Wallet $wallet): string;
14+
15+
public function amount(Wallet $wallet): string;
16+
17+
/** @param float|int|string $value */
18+
public function sync(Wallet $wallet, $value): bool;
19+
20+
/** @param float|int|string $value */
21+
public function increase(Wallet $wallet, $value): string;
22+
}

src/Services/StateService.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Bavix\Wallet\Services;
6+
7+
use Bavix\Wallet\Models\Wallet;
8+
9+
final class StateService implements StateServiceInterface
10+
{
11+
private BookkeeperServiceInterface $bookkeeperService;
12+
private RegulatorServiceInterface $regulatorService;
13+
14+
/** @var Wallet[] */
15+
private array $wallets = [];
16+
17+
public function __construct(
18+
BookkeeperServiceInterface $bookkeeperService,
19+
RegulatorServiceInterface $regulatorService
20+
) {
21+
$this->bookkeeperService = $bookkeeperService;
22+
$this->regulatorService = $regulatorService;
23+
}
24+
25+
public function persist(Wallet $wallet): void
26+
{
27+
$this->wallets[] = $wallet;
28+
}
29+
30+
public function commit(): void
31+
{
32+
foreach (array_unique($this->wallets) as $wallet) {
33+
$this->bookkeeperService->increase($wallet, $this->regulatorService->diff($wallet));
34+
$this->regulatorService->missing($wallet);
35+
}
36+
}
37+
38+
public function purge(): void
39+
{
40+
foreach ($this->wallets as $wallet) {
41+
$this->regulatorService->missing($wallet);
42+
}
43+
44+
$this->wallets = [];
45+
}
46+
}

0 commit comments

Comments
 (0)