From a032f032381ffda88b5a59ed1d499799b5652368 Mon Sep 17 00:00:00 2001 From: Anton Levchenko Date: Thu, 15 Apr 2021 11:18:35 +0300 Subject: [PATCH 1/5] Task 1 --- .../java/com/devexperts/account/Account.java | 10 +++++---- .../AccountAlreadyExistsExeption.java | 8 +++++++ .../devexperts/service/AccountService.java | 7 ++++-- .../service/AccountServiceImpl.java | 22 ++++++++++++------- 4 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/devexperts/exeptions/AccountAlreadyExistsExeption.java diff --git a/src/main/java/com/devexperts/account/Account.java b/src/main/java/com/devexperts/account/Account.java index fb2a3af..988cfaa 100644 --- a/src/main/java/com/devexperts/account/Account.java +++ b/src/main/java/com/devexperts/account/Account.java @@ -1,12 +1,14 @@ package com.devexperts.account; +import java.math.BigDecimal; + public class Account { private final AccountKey accountKey; private final String firstName; private final String lastName; - private Double balance; + private BigDecimal balance; - public Account(AccountKey accountKey, String firstName, String lastName, Double balance) { + public Account(AccountKey accountKey, String firstName, String lastName, BigDecimal balance) { this.accountKey = accountKey; this.firstName = firstName; this.lastName = lastName; @@ -25,11 +27,11 @@ public String getLastName() { return lastName; } - public Double getBalance() { + public BigDecimal getBalance() { return balance; } - public void setBalance(Double balance) { + public void setBalance(BigDecimal balance) { this.balance = balance; } } diff --git a/src/main/java/com/devexperts/exeptions/AccountAlreadyExistsExeption.java b/src/main/java/com/devexperts/exeptions/AccountAlreadyExistsExeption.java new file mode 100644 index 0000000..7f4e369 --- /dev/null +++ b/src/main/java/com/devexperts/exeptions/AccountAlreadyExistsExeption.java @@ -0,0 +1,8 @@ +package com.devexperts.exeptions; + +public class AccountAlreadyExistsExeption extends RuntimeException { + public AccountAlreadyExistsExeption(){ + super("Account already exists"); + } + +} diff --git a/src/main/java/com/devexperts/service/AccountService.java b/src/main/java/com/devexperts/service/AccountService.java index f287597..9622895 100644 --- a/src/main/java/com/devexperts/service/AccountService.java +++ b/src/main/java/com/devexperts/service/AccountService.java @@ -1,6 +1,9 @@ package com.devexperts.service; import com.devexperts.account.Account; +import com.devexperts.account.AccountKey; + +import java.math.BigDecimal; public interface AccountService { @@ -16,7 +19,7 @@ public interface AccountService { * @param account account entity to add or update * @throws IllegalArgumentException if account is already present * */ - void createAccount(Account account); + void createAccount(AccountKey accountKey, Account account); /** * Get account from the cache @@ -33,5 +36,5 @@ public interface AccountService { * @param target account to transfer money to * @param amount dollar amount to transfer * */ - void transfer(Account source, Account target, double amount); + void transfer(Account source, Account target, BigDecimal amount); } diff --git a/src/main/java/com/devexperts/service/AccountServiceImpl.java b/src/main/java/com/devexperts/service/AccountServiceImpl.java index 91261ba..62c07d4 100644 --- a/src/main/java/com/devexperts/service/AccountServiceImpl.java +++ b/src/main/java/com/devexperts/service/AccountServiceImpl.java @@ -2,15 +2,19 @@ import com.devexperts.account.Account; import com.devexperts.account.AccountKey; +import com.devexperts.exeptions.AccountAlreadyExistsExeption; import org.springframework.stereotype.Service; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; @Service public class AccountServiceImpl implements AccountService { - private final List accounts = new ArrayList<>(); + private final Map accounts = new ConcurrentHashMap<>(); @Override public void clear() { @@ -18,20 +22,22 @@ public void clear() { } @Override - public void createAccount(Account account) { - accounts.add(account); + public void createAccount(AccountKey accountKey, Account account) { + if (accountKey == null || account == null){ + throw new IllegalArgumentException("Account or AccountKey is invalid"); + } + if (accounts.putIfAbsent(accountKey, account) == null){ + throw new AccountAlreadyExistsExeption(); + } } @Override public Account getAccount(long id) { - return accounts.stream() - .filter(account -> account.getAccountKey() == AccountKey.valueOf(id)) - .findAny() - .orElse(null); + return accounts.get(AccountKey.valueOf(id)); } @Override - public void transfer(Account source, Account target, double amount) { + public void transfer(Account source, Account target, BigDecimal amount) { //do nothing for now } } From 243712113e45af20f050afae53d031bccb2bc52f Mon Sep 17 00:00:00 2001 From: Anton Levchenko Date: Thu, 15 Apr 2021 11:24:36 +0300 Subject: [PATCH 2/5] Task 2 --- src/main/java/com/devexperts/service/AccountServiceImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/devexperts/service/AccountServiceImpl.java b/src/main/java/com/devexperts/service/AccountServiceImpl.java index 62c07d4..9ea9e15 100644 --- a/src/main/java/com/devexperts/service/AccountServiceImpl.java +++ b/src/main/java/com/devexperts/service/AccountServiceImpl.java @@ -38,6 +38,7 @@ public Account getAccount(long id) { @Override public void transfer(Account source, Account target, BigDecimal amount) { - //do nothing for now + source.setBalance(source.getBalance().subtract(amount)); + target.setBalance(target.getBalance().add(amount)); } } From 7ac620f8d31e7b8417f9dad8d6af0b9dd417dc0b Mon Sep 17 00:00:00 2001 From: Anton Levchenko Date: Thu, 15 Apr 2021 12:02:00 +0300 Subject: [PATCH 3/5] Task 3 --- .../com/devexperts/account/AccountKey.java | 4 +++ .../AccountAlreadyExistsException.java | 8 +++++ .../AccountAlreadyExistsExeption.java | 8 ----- .../exeptions/NotEnoughFundsException.java | 7 +++++ .../service/AccountServiceImpl.java | 30 +++++++++++++++---- 5 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/devexperts/exeptions/AccountAlreadyExistsException.java delete mode 100644 src/main/java/com/devexperts/exeptions/AccountAlreadyExistsExeption.java create mode 100644 src/main/java/com/devexperts/exeptions/NotEnoughFundsException.java diff --git a/src/main/java/com/devexperts/account/AccountKey.java b/src/main/java/com/devexperts/account/AccountKey.java index 1b0a233..365c524 100644 --- a/src/main/java/com/devexperts/account/AccountKey.java +++ b/src/main/java/com/devexperts/account/AccountKey.java @@ -10,6 +10,10 @@ public class AccountKey { private final long accountId; + public long getAccountId() { + return accountId; + } + private AccountKey(long accountId) { this.accountId = accountId; } diff --git a/src/main/java/com/devexperts/exeptions/AccountAlreadyExistsException.java b/src/main/java/com/devexperts/exeptions/AccountAlreadyExistsException.java new file mode 100644 index 0000000..e713259 --- /dev/null +++ b/src/main/java/com/devexperts/exeptions/AccountAlreadyExistsException.java @@ -0,0 +1,8 @@ +package com.devexperts.exeptions; + +public class AccountAlreadyExistsException extends RuntimeException { + public AccountAlreadyExistsException(){ + super("Account already exists"); + } + +} diff --git a/src/main/java/com/devexperts/exeptions/AccountAlreadyExistsExeption.java b/src/main/java/com/devexperts/exeptions/AccountAlreadyExistsExeption.java deleted file mode 100644 index 7f4e369..0000000 --- a/src/main/java/com/devexperts/exeptions/AccountAlreadyExistsExeption.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.devexperts.exeptions; - -public class AccountAlreadyExistsExeption extends RuntimeException { - public AccountAlreadyExistsExeption(){ - super("Account already exists"); - } - -} diff --git a/src/main/java/com/devexperts/exeptions/NotEnoughFundsException.java b/src/main/java/com/devexperts/exeptions/NotEnoughFundsException.java new file mode 100644 index 0000000..205309f --- /dev/null +++ b/src/main/java/com/devexperts/exeptions/NotEnoughFundsException.java @@ -0,0 +1,7 @@ +package com.devexperts.exeptions; + +public class NotEnoughFundsException extends RuntimeException { + public NotEnoughFundsException(){ + super("Not Enough Funds"); + } +} diff --git a/src/main/java/com/devexperts/service/AccountServiceImpl.java b/src/main/java/com/devexperts/service/AccountServiceImpl.java index 9ea9e15..c973ffb 100644 --- a/src/main/java/com/devexperts/service/AccountServiceImpl.java +++ b/src/main/java/com/devexperts/service/AccountServiceImpl.java @@ -2,12 +2,11 @@ import com.devexperts.account.Account; import com.devexperts.account.AccountKey; -import com.devexperts.exeptions.AccountAlreadyExistsExeption; +import com.devexperts.exeptions.AccountAlreadyExistsException; +import com.devexperts.exeptions.NotEnoughFundsException; import org.springframework.stereotype.Service; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -27,7 +26,7 @@ public void createAccount(AccountKey accountKey, Account account) { throw new IllegalArgumentException("Account or AccountKey is invalid"); } if (accounts.putIfAbsent(accountKey, account) == null){ - throw new AccountAlreadyExistsExeption(); + throw new AccountAlreadyExistsException(); } } @@ -36,9 +35,30 @@ public Account getAccount(long id) { return accounts.get(AccountKey.valueOf(id)); } - @Override + /*@Override public void transfer(Account source, Account target, BigDecimal amount) { source.setBalance(source.getBalance().subtract(amount)); target.setBalance(target.getBalance().add(amount)); + }*/ + + @Override + public void transfer(final Account source, final Account target, final BigDecimal amount) { + if (source == null || target == null || amount == null){ + throw new IllegalArgumentException("account or accountKey or amount is invalid"); + } + if (source.getBalance().subtract(amount).compareTo(new BigDecimal("0")) < 0) { + throw new NotEnoughFundsException(); + } + final boolean prioritySourceTarget = source.getAccountKey().getAccountId() > target.getAccountKey().getAccountId(); + synchronized (prioritySourceTarget ? source : target) { + synchronized (prioritySourceTarget ? target : source) { + doTransfer(source, target, amount); + } + } + } + + private void doTransfer(Account source, Account target, BigDecimal amount) { + source.setBalance(source.getBalance().subtract(amount)); + target.setBalance(target.getBalance().add(amount)); } } From 42be3669a6bb68b1f0d56cf726036bc0c36f1c5a Mon Sep 17 00:00:00 2001 From: Anton Levchenko Date: Thu, 15 Apr 2021 13:19:23 +0300 Subject: [PATCH 4/5] Task 4 --- .../devexperts/rest/AccountController.java | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/devexperts/rest/AccountController.java b/src/main/java/com/devexperts/rest/AccountController.java index b300282..23d49d4 100644 --- a/src/main/java/com/devexperts/rest/AccountController.java +++ b/src/main/java/com/devexperts/rest/AccountController.java @@ -1,14 +1,42 @@ package com.devexperts.rest; +import com.devexperts.account.Account; +import com.devexperts.exeptions.NotEnoughFundsException; +import com.devexperts.service.AccountService; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.math.BigDecimal; + @RestController @RequestMapping("/api") public class AccountController extends AbstractAccountController { - public ResponseEntity transfer(long sourceId, long targetId, double amount) { - return null; + private AccountService accountService; + + public AccountController(AccountService accountService) { + this.accountService = accountService; + } + @PostMapping(path = "/operations/transfer") + public ResponseEntity transfer(@RequestParam("source_id") long sourceId, + @RequestParam("target_id") long targetId, + @RequestParam("amount") double amount) + { + Account accountSource = accountService.getAccount(sourceId); + Account accountTarget = accountService.getAccount(targetId); + if (accountSource == null || accountTarget == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + try { + accountService.transfer(accountSource, accountTarget, BigDecimal.valueOf(amount)); + } catch (NotEnoughFundsException e) { + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } + + return new ResponseEntity<>(HttpStatus.OK); } } From 2adca61bfdf025c7e0a14e49b31ca4fa49c9c9fe Mon Sep 17 00:00:00 2001 From: Anton Levchenko Date: Thu, 15 Apr 2021 13:55:41 +0300 Subject: [PATCH 5/5] Task 5 --- src/main/resources/sql/data/accounts.sql | 6 ++++++ src/main/resources/sql/data/select.sql | 1 + src/main/resources/sql/data/transfers.sql | 7 +++++++ 3 files changed, 14 insertions(+) create mode 100644 src/main/resources/sql/data/accounts.sql create mode 100644 src/main/resources/sql/data/select.sql create mode 100644 src/main/resources/sql/data/transfers.sql diff --git a/src/main/resources/sql/data/accounts.sql b/src/main/resources/sql/data/accounts.sql new file mode 100644 index 0000000..0f52c04 --- /dev/null +++ b/src/main/resources/sql/data/accounts.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS accounts ( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + first_name VARCHAR(255) NOT NULL, + last_name VARCHAR(255) NOT NULL, + balance BIGINT NOT NULL +); \ No newline at end of file diff --git a/src/main/resources/sql/data/select.sql b/src/main/resources/sql/data/select.sql new file mode 100644 index 0000000..b6baf61 --- /dev/null +++ b/src/main/resources/sql/data/select.sql @@ -0,0 +1 @@ +SELECT source_id FROM transfers GROUP BY source_id HAVING SUM(amount)>=1000 WHERE transfer_time>'2019-01-01'::timestamp; \ No newline at end of file diff --git a/src/main/resources/sql/data/transfers.sql b/src/main/resources/sql/data/transfers.sql new file mode 100644 index 0000000..07ab731 --- /dev/null +++ b/src/main/resources/sql/data/transfers.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS transfers( + id BIGINT AUTO_INCREMENT PRIMARY KEY, + source_id BIGINT NOT NULL, + target_id BIGINT NOT NULL, + amount BIGINT NOT NULL, + transfer_time TIMESTAMP NOT NULL, +); \ No newline at end of file