From 4bd2763dab076566d202f459bcd05943fa79cc95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galina=C2=89Is?= Date: Sun, 28 Feb 2021 13:40:37 +0300 Subject: [PATCH 1/6] =?UTF-8?q?=D0=92=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4?= =?UTF-8?q?=D0=B5=20getAccount=20=D1=81=D1=80=D0=B0=D0=B2=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=20=D1=81=D1=81=D1=8B=D0=BB?= =?UTF-8?q?=D0=BA=D0=B5=20=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=BE?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2=20=D0=BC=D0=B5?= =?UTF-8?q?=D1=82=D0=BE=D0=B4=D0=B0=20equals.=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D1=8E=D0=BD=D0=B8=D1=82-?= =?UTF-8?q?=D1=82=D0=B5=D1=81=D1=82=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D0=BF?= =?UTF-8?q?=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B8=20=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=8B=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D0=B0=20get?= =?UTF-8?q?Account.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/devexperts/account/AccountKey.java | 18 ++++++++- .../service/AccountServiceImpl.java | 2 +- .../service/AccountServiceImplTest.java | 38 +++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 src/test/java/com/devexperts/service/AccountServiceImplTest.java diff --git a/src/main/java/com/devexperts/account/AccountKey.java b/src/main/java/com/devexperts/account/AccountKey.java index 1b0a233..207c095 100644 --- a/src/main/java/com/devexperts/account/AccountKey.java +++ b/src/main/java/com/devexperts/account/AccountKey.java @@ -6,7 +6,7 @@ *

* NOTE: we suspect that later {@link #accountId} is not going to be uniquely identifying an account, * as we might add human-readable account representation and some clearing codes for partners. - * */ + */ public class AccountKey { private final long accountId; @@ -17,4 +17,20 @@ private AccountKey(long accountId) { public static AccountKey valueOf(long accountId) { return new AccountKey(accountId); } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof AccountKey)) + return false; + AccountKey accountKey = (AccountKey) obj; + return accountId == accountKey.accountId; + } + + @Override + public int hashCode() { + return Long.hashCode(accountId); + } } diff --git a/src/main/java/com/devexperts/service/AccountServiceImpl.java b/src/main/java/com/devexperts/service/AccountServiceImpl.java index 91261ba..05211b4 100644 --- a/src/main/java/com/devexperts/service/AccountServiceImpl.java +++ b/src/main/java/com/devexperts/service/AccountServiceImpl.java @@ -25,7 +25,7 @@ public void createAccount(Account account) { @Override public Account getAccount(long id) { return accounts.stream() - .filter(account -> account.getAccountKey() == AccountKey.valueOf(id)) + .filter(account -> account.getAccountKey().equals(AccountKey.valueOf(id))) .findAny() .orElse(null); } diff --git a/src/test/java/com/devexperts/service/AccountServiceImplTest.java b/src/test/java/com/devexperts/service/AccountServiceImplTest.java new file mode 100644 index 0000000..23ee2af --- /dev/null +++ b/src/test/java/com/devexperts/service/AccountServiceImplTest.java @@ -0,0 +1,38 @@ +package com.devexperts.service; + +import com.devexperts.account.Account; +import com.devexperts.account.AccountKey; +import org.junit.Assert; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; + +class AccountServiceImplTest { + private AccountServiceImpl accountService = new AccountServiceImpl(); + + @BeforeEach + void init() { + accountService.createAccount(new Account(AccountKey.valueOf(1L), "Ivan", "Ivanov", 500.0)); + accountService.createAccount(new Account(AccountKey.valueOf(2L), "Petr", "Petrov", 350.0)); + accountService.createAccount(new Account(AccountKey.valueOf(3L), "Igor", "Sidorov", 750.0)); + } + + @org.junit.jupiter.api.Test + void getAccountFromAccounts() { + Account account = accountService.getAccount(1L); + Assert.assertNotNull(account); + Assert.assertEquals("Ivan", account.getFirstName()); + Assert.assertEquals("Ivanov", account.getLastName()); + Assert.assertEquals(500.0, account.getBalance(), 0.00001); + } + + @org.junit.jupiter.api.Test + void getAccountNotFromAccounts() { + Account account = accountService.getAccount(5L); + Assert.assertNull(account); + } + + @AfterEach + void tearDown() { + accountService.clear(); + } +} \ No newline at end of file From cb70c5d26f54b65eebff7443e670c1a8287e6f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galina=C2=89Is?= Date: Sun, 28 Feb 2021 14:41:39 +0300 Subject: [PATCH 2/6] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=20transfer?= =?UTF-8?q?=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D1=8B=20?= =?UTF-8?q?=D0=B2=20=D0=BE=D0=B4=D0=BD=D0=BE=D0=BF=D0=BE=D1=82=D0=BE=D1=87?= =?UTF-8?q?=D0=BD=D0=BE=D0=B9=20=D1=81=D1=80=D0=B5=D0=B4=D0=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/AccountServiceImpl.java | 14 ++++++- .../exception/InvalidAmountException.java | 4 ++ .../exception/NotFoundAccountException.java | 9 +++++ .../service/AccountServiceImplTest.java | 40 +++++++++++++++++-- 4 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/devexperts/service/exception/InvalidAmountException.java create mode 100644 src/main/java/com/devexperts/service/exception/NotFoundAccountException.java diff --git a/src/main/java/com/devexperts/service/AccountServiceImpl.java b/src/main/java/com/devexperts/service/AccountServiceImpl.java index 05211b4..b3bc457 100644 --- a/src/main/java/com/devexperts/service/AccountServiceImpl.java +++ b/src/main/java/com/devexperts/service/AccountServiceImpl.java @@ -2,6 +2,8 @@ import com.devexperts.account.Account; import com.devexperts.account.AccountKey; +import com.devexperts.service.exception.InvalidAmountException; +import com.devexperts.service.exception.NotFoundAccountException; import org.springframework.stereotype.Service; import java.util.ArrayList; @@ -32,6 +34,16 @@ public Account getAccount(long id) { @Override public void transfer(Account source, Account target, double amount) { - //do nothing for now + if (!accounts.contains(source)) { + throw new NotFoundAccountException(source); + } + if (!accounts.contains(target)) { + throw new NotFoundAccountException(target); + } + if (source.getBalance() < amount) { + throw new InvalidAmountException(); + } + source.setBalance(source.getBalance() - amount); + target.setBalance(target.getBalance() + amount); } } diff --git a/src/main/java/com/devexperts/service/exception/InvalidAmountException.java b/src/main/java/com/devexperts/service/exception/InvalidAmountException.java new file mode 100644 index 0000000..6bdd45f --- /dev/null +++ b/src/main/java/com/devexperts/service/exception/InvalidAmountException.java @@ -0,0 +1,4 @@ +package com.devexperts.service.exception; + +public class InvalidAmountException extends RuntimeException { +} diff --git a/src/main/java/com/devexperts/service/exception/NotFoundAccountException.java b/src/main/java/com/devexperts/service/exception/NotFoundAccountException.java new file mode 100644 index 0000000..0b9bbfd --- /dev/null +++ b/src/main/java/com/devexperts/service/exception/NotFoundAccountException.java @@ -0,0 +1,9 @@ +package com.devexperts.service.exception; + +import com.devexperts.account.Account; + +public class NotFoundAccountException extends RuntimeException { + public NotFoundAccountException(Account account) { + super("Not found account " + account + " at AccountService"); + } +} diff --git a/src/test/java/com/devexperts/service/AccountServiceImplTest.java b/src/test/java/com/devexperts/service/AccountServiceImplTest.java index 23ee2af..9f15b2e 100644 --- a/src/test/java/com/devexperts/service/AccountServiceImplTest.java +++ b/src/test/java/com/devexperts/service/AccountServiceImplTest.java @@ -2,18 +2,29 @@ import com.devexperts.account.Account; import com.devexperts.account.AccountKey; +import com.devexperts.service.exception.InvalidAmountException; +import com.devexperts.service.exception.NotFoundAccountException; import org.junit.Assert; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; class AccountServiceImplTest { + private static final double DELTA = 0.00001; + private AccountServiceImpl accountService = new AccountServiceImpl(); + private Account account1; + private Account account2; + private Account account3; @BeforeEach void init() { - accountService.createAccount(new Account(AccountKey.valueOf(1L), "Ivan", "Ivanov", 500.0)); - accountService.createAccount(new Account(AccountKey.valueOf(2L), "Petr", "Petrov", 350.0)); - accountService.createAccount(new Account(AccountKey.valueOf(3L), "Igor", "Sidorov", 750.0)); + account1 = new Account(AccountKey.valueOf(1L), "Ivan", "Ivanov", 500.0); + account2 = new Account(AccountKey.valueOf(2L), "Petr", "Petrov", 350.0); + account3 = new Account(AccountKey.valueOf(3L), "Igor", "Sidorov", 750.0); + accountService.createAccount(account1); + accountService.createAccount(account2); + accountService.createAccount(account3); } @org.junit.jupiter.api.Test @@ -22,7 +33,7 @@ void getAccountFromAccounts() { Assert.assertNotNull(account); Assert.assertEquals("Ivan", account.getFirstName()); Assert.assertEquals("Ivanov", account.getLastName()); - Assert.assertEquals(500.0, account.getBalance(), 0.00001); + Assert.assertEquals(500.0, account.getBalance(), DELTA); } @org.junit.jupiter.api.Test @@ -31,6 +42,27 @@ void getAccountNotFromAccounts() { Assert.assertNull(account); } + @org.junit.jupiter.api.Test + void validTransfer() { + accountService.transfer(account1, account2, 200); + Assert.assertEquals(300, account1.getBalance(), DELTA); + Assert.assertEquals(550, account2.getBalance(), DELTA); + } + + @org.junit.jupiter.api.Test + void invalidAmountTransfer() { + Assertions.assertThrows(InvalidAmountException.class, () -> { + accountService.transfer(account2, account3, 500); + }); + } + + @org.junit.jupiter.api.Test + void transferFromAccountNotFromAccounts() { + Assertions.assertThrows(NotFoundAccountException.class, () -> { + accountService.transfer(new Account(AccountKey.valueOf(8L), "NoName", "NoSurname", 1500.0), account3, 500); + }); + } + @AfterEach void tearDown() { accountService.clear(); From 385c40d437f63613794a02105eff94ccc2df5bb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galina=C2=89Is?= Date: Sun, 28 Feb 2021 14:19:11 +0300 Subject: [PATCH 3/6] =?UTF-8?q?=D0=9C=D0=BE=D0=B4=D0=B8=D1=84=D0=B8=D1=86?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE?= =?UTF-8?q?=D0=B4=20transfer=20=D0=B4=D0=BB=D1=8F=20=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D1=8B=20=D0=B2=20=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=D0=BF=D0=BE=D1=82=D0=BE=D1=87=D0=BD=D0=BE=D0=B9=20=D1=81=D1=80?= =?UTF-8?q?=D0=B5=D0=B4=D0=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/devexperts/account/AccountKey.java | 7 ++++- .../service/AccountServiceImpl.java | 16 ++++++++++++ .../service/AccountServiceImplTest.java | 26 +++++++++++++++++-- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/devexperts/account/AccountKey.java b/src/main/java/com/devexperts/account/AccountKey.java index 207c095..1038e7a 100644 --- a/src/main/java/com/devexperts/account/AccountKey.java +++ b/src/main/java/com/devexperts/account/AccountKey.java @@ -7,7 +7,7 @@ * NOTE: we suspect that later {@link #accountId} is not going to be uniquely identifying an account, * as we might add human-readable account representation and some clearing codes for partners. */ -public class AccountKey { +public class AccountKey implements Comparable { private final long accountId; private AccountKey(long accountId) { @@ -33,4 +33,9 @@ public boolean equals(Object obj) { public int hashCode() { return Long.hashCode(accountId); } + + @Override + public int compareTo(AccountKey accountKey) { + return Long.compare(accountId, accountKey.accountId); + } } diff --git a/src/main/java/com/devexperts/service/AccountServiceImpl.java b/src/main/java/com/devexperts/service/AccountServiceImpl.java index b3bc457..5dc1c4b 100644 --- a/src/main/java/com/devexperts/service/AccountServiceImpl.java +++ b/src/main/java/com/devexperts/service/AccountServiceImpl.java @@ -34,6 +34,22 @@ public Account getAccount(long id) { @Override public void transfer(Account source, Account target, double amount) { + if (source.getAccountKey().compareTo(target.getAccountKey()) >= 0) { + synchronized (source) { + synchronized (target) { + doTransfer(source, target, amount); + } + } + } else { + synchronized (target) { + synchronized (source) { + doTransfer(source, target, amount); + } + } + } + } + + private void doTransfer(Account source, Account target, double amount) { if (!accounts.contains(source)) { throw new NotFoundAccountException(source); } diff --git a/src/test/java/com/devexperts/service/AccountServiceImplTest.java b/src/test/java/com/devexperts/service/AccountServiceImplTest.java index 9f15b2e..219203f 100644 --- a/src/test/java/com/devexperts/service/AccountServiceImplTest.java +++ b/src/test/java/com/devexperts/service/AccountServiceImplTest.java @@ -9,12 +9,15 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + class AccountServiceImplTest { private static final double DELTA = 0.00001; private AccountServiceImpl accountService = new AccountServiceImpl(); - private Account account1; - private Account account2; + private volatile Account account1; + private volatile Account account2; private Account account3; @BeforeEach @@ -63,6 +66,25 @@ void transferFromAccountNotFromAccounts() { }); } + @org.junit.jupiter.api.Test + void transferMultiThread() throws InterruptedException { + int countTransfer = 5; + CountDownLatch countDownLatch = new CountDownLatch(countTransfer); + Runnable task = () -> { + try { + accountService.transfer(account1, account2, 100); + } finally { + countDownLatch.countDown(); + } + }; + for (int i = 0; i < countTransfer; i++) { + new Thread(task).start(); + } + countDownLatch.await(5, TimeUnit.SECONDS); + Assert.assertEquals(0, account1.getBalance(), DELTA); + Assert.assertEquals(850, account2.getBalance(), DELTA); + } + @AfterEach void tearDown() { accountService.clear(); From d2185427dfbc5dc7cb2d1c20eef1f332b94327c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galina=C2=89Is?= Date: Sun, 28 Feb 2021 15:18:11 +0300 Subject: [PATCH 4/6] =?UTF-8?q?=D0=9C=D0=BE=D0=B4=D0=B8=D1=84=D0=B8=D1=86?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=20=D0=BC=D0=B5=D1=82=D0=BE?= =?UTF-8?q?=D0=B4=20getAccount=20-=20=D0=B5=D1=81=D0=BB=D0=B8=20=D0=B0?= =?UTF-8?q?=D0=BA=D0=BA=D0=B0=D1=83=D0=BD=D1=82=20=D1=81=20=D1=83=D0=BA?= =?UTF-8?q?=D0=B0=D0=B7=D0=B0=D0=BD=D0=BD=D1=8B=D0=BC=20id=20=D0=BD=D0=B5?= =?UTF-8?q?=20=D0=BD=D0=B0=D0=B9=D0=B4=D0=B5=D0=BD,=20=D0=B1=D1=80=D0=BE?= =?UTF-8?q?=D1=81=D0=B0=D0=B5=D1=82=D1=81=D1=8F=20=D0=B8=D1=81=D0=BA=D0=BB?= =?UTF-8?q?=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D0=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/devexperts/service/AccountServiceImpl.java | 2 +- .../service/exception/NotFoundAccountException.java | 4 ++++ .../java/com/devexperts/service/AccountServiceImplTest.java | 6 ++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/devexperts/service/AccountServiceImpl.java b/src/main/java/com/devexperts/service/AccountServiceImpl.java index 5dc1c4b..04e4dc6 100644 --- a/src/main/java/com/devexperts/service/AccountServiceImpl.java +++ b/src/main/java/com/devexperts/service/AccountServiceImpl.java @@ -29,7 +29,7 @@ public Account getAccount(long id) { return accounts.stream() .filter(account -> account.getAccountKey().equals(AccountKey.valueOf(id))) .findAny() - .orElse(null); + .orElseThrow(() -> new NotFoundAccountException(id)); } @Override diff --git a/src/main/java/com/devexperts/service/exception/NotFoundAccountException.java b/src/main/java/com/devexperts/service/exception/NotFoundAccountException.java index 0b9bbfd..2f72c98 100644 --- a/src/main/java/com/devexperts/service/exception/NotFoundAccountException.java +++ b/src/main/java/com/devexperts/service/exception/NotFoundAccountException.java @@ -6,4 +6,8 @@ public class NotFoundAccountException extends RuntimeException { public NotFoundAccountException(Account account) { super("Not found account " + account + " at AccountService"); } + + public NotFoundAccountException(Long id) { + super("Not found account with id = " + id + " at AccountService"); + } } diff --git a/src/test/java/com/devexperts/service/AccountServiceImplTest.java b/src/test/java/com/devexperts/service/AccountServiceImplTest.java index 219203f..3e92839 100644 --- a/src/test/java/com/devexperts/service/AccountServiceImplTest.java +++ b/src/test/java/com/devexperts/service/AccountServiceImplTest.java @@ -41,8 +41,10 @@ void getAccountFromAccounts() { @org.junit.jupiter.api.Test void getAccountNotFromAccounts() { - Account account = accountService.getAccount(5L); - Assert.assertNull(account); + NotFoundAccountException exception = Assertions.assertThrows(NotFoundAccountException.class, () -> { + accountService.getAccount(5L); + }); + Assert.assertEquals(exception.getMessage(), "Not found account with id = 5 at AccountService"); } @org.junit.jupiter.api.Test From b71aab03935d419d7682d391added8ca21af7e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galina=C2=89Is?= Date: Sun, 28 Feb 2021 16:05:06 +0300 Subject: [PATCH 5/6] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=20=D0=BF=D0=B5=D1=80=D0=B5=D0=B2=D0=BE=D0=B4?= =?UTF-8?q?=20=D0=BC=D0=B5=D0=B6=D0=B4=D1=83=20=D0=B0=D0=BA=D0=BA=D0=B0?= =?UTF-8?q?=D1=83=D0=BD=D1=82=D0=B0=D0=BC=D0=B8=20=D1=87=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B7=20Rest-=D0=B7=D0=B0=D0=BF=D1=80=D0=BE=D1=81.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/PopulationAccountService.java | 20 +++++++++++++ .../rest/AbstractAccountController.java | 3 +- .../devexperts/rest/AccountController.java | 28 +++++++++++++++++-- 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/devexperts/config/PopulationAccountService.java diff --git a/src/main/java/com/devexperts/config/PopulationAccountService.java b/src/main/java/com/devexperts/config/PopulationAccountService.java new file mode 100644 index 0000000..f52c052 --- /dev/null +++ b/src/main/java/com/devexperts/config/PopulationAccountService.java @@ -0,0 +1,20 @@ +package com.devexperts.config; + +import com.devexperts.account.Account; +import com.devexperts.account.AccountKey; +import com.devexperts.service.AccountService; +import org.springframework.boot.CommandLineRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class PopulationAccountService { + @Bean + CommandLineRunner initClientDatabase(AccountService accountService) { + return args -> { + accountService.createAccount(new Account(AccountKey.valueOf(1L), "Ivan", "Ivanov", 500.0)); + accountService.createAccount(new Account(AccountKey.valueOf(2L), "Petr", "Petrov", 350.0)); + accountService.createAccount(new Account(AccountKey.valueOf(3L), "Igor", "Sidorov", 750.0)); + }; + } +} diff --git a/src/main/java/com/devexperts/rest/AbstractAccountController.java b/src/main/java/com/devexperts/rest/AbstractAccountController.java index dea5a3c..7b1c4a4 100644 --- a/src/main/java/com/devexperts/rest/AbstractAccountController.java +++ b/src/main/java/com/devexperts/rest/AbstractAccountController.java @@ -1,7 +1,8 @@ package com.devexperts.rest; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; public abstract class AbstractAccountController { - abstract ResponseEntity transfer(long sourceId, long targetId, double amount); + abstract ResponseEntity transfer(@PathVariable Long sourceId, @PathVariable Long targetId, @PathVariable Double amount); } diff --git a/src/main/java/com/devexperts/rest/AccountController.java b/src/main/java/com/devexperts/rest/AccountController.java index b300282..52f24c6 100644 --- a/src/main/java/com/devexperts/rest/AccountController.java +++ b/src/main/java/com/devexperts/rest/AccountController.java @@ -1,14 +1,38 @@ package com.devexperts.rest; +import com.devexperts.account.Account; +import com.devexperts.service.AccountService; +import com.devexperts.service.exception.NotFoundAccountException; +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; @RestController @RequestMapping("/api") public class AccountController extends AbstractAccountController { + private final AccountService accountService; - public ResponseEntity transfer(long sourceId, long targetId, double amount) { - return null; + public AccountController(AccountService accountService) { + this.accountService = accountService; + } + + @PostMapping("/operations/transfer") + public ResponseEntity transfer(@RequestParam Long sourceId, @RequestParam Long targetId, @RequestParam Double amount) { + if (sourceId == null || targetId == null || amount == null || amount <= 0) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + try { + Account source = accountService.getAccount(sourceId); + Account target = accountService.getAccount(targetId); + accountService.transfer(source, target, amount); + return new ResponseEntity<>(HttpStatus.OK); + } catch (NotFoundAccountException e) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } catch (Exception e) { + return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + } } } From c39f6d2722f4123d73a754c525e2b051508e79ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Galina=C2=89Is?= Date: Sun, 28 Feb 2021 16:29:50 +0300 Subject: [PATCH 6/6] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=81=D0=BA=D1=80=D0=B8=D0=BF=D1=82=D1=8B.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/sql/data/accounts.sql | 9 +++++++++ src/main/resources/sql/data/select.sql | 1 + src/main/resources/sql/data/transfers.sql | 12 ++++++++++++ 3 files changed, 22 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..1f07a0a --- /dev/null +++ b/src/main/resources/sql/data/accounts.sql @@ -0,0 +1,9 @@ +DROP TABLE IF EXISTS accounts; + +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..d1b5579 --- /dev/null +++ b/src/main/resources/sql/data/select.sql @@ -0,0 +1 @@ +SELECT SOURCE_ID FROM transfers WHERE TRANSFER_TIME > '2019-01-01'::timestamp GROUP BY SOURCE_ID HAVING sum(AMOUNT) > 1000; \ 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..d3728be --- /dev/null +++ b/src/main/resources/sql/data/transfers.sql @@ -0,0 +1,12 @@ +DROP TABLE IF EXISTS transfers; + +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(6) NOT NULL, + FOREIGN KEY (SOURCE_ID) REFERENCES accounts (ID) ON UPDATE CASCADE ON DELETE SET NULL, + FOREIGN KEY (TARGET_ID) REFERENCES accounts (ID) ON UPDATE CASCADE ON DELETE SET NULL +); \ No newline at end of file