video.transaction-manager.demo.mp4
Всего 5 сервисов
3
сервиса конфигурации:
-
service-registry
: механизм регистрации и обнаружения сервисов- сервер Eureka (
@EnableEurekaServer
/ для обнаружения сервиса@EnableDiscoveryClient
) - [docs:] Service Registry / Discovery Pattern
- сервер Eureka (
-
config-server
: управление config сервисовspring-cloud-config-server
(maven)- [docs:] Spring Cloud Config Server
-
api-gateway
: общий порт (port:8060
) для всех сервисовspring-cloud-starter-gateway
(maven)- [docs:] API Gateway Pattern
2
сервиса с бизнес-логикой:
transaction-service
: отвечает за прием и обработку "Транзакций", и запросов от "Клиента"currency-service
: отвечает за получение актуальных "курсов валют" (обращяется кВнешнему API
)Info: API для получения курсов валют https://openexchangerates.org/api
transaction-service
-
Основные Классы:
model:
- class
Transaction
: объект для работы с "Транзакциями" - class
Limit
: объект для работы с "Лимитами" - enum
ExpenseCategory
: для определения доступных категорий расходов "Транзакциями"Note: "Транзакциями" не сохраняется если не соответствует хотя-бы одной из категорий
- class
Currency
: объект для работы с "курсами валют" изcurrency-service
имеет доп параметр CurrencyRequest, котоый содержит доп информацию для конкретной валюты (валютная пара -
"base":"USD"
, дата курса -"formatted_timestamp": "2024-04-26"
) - class
ExceededTransactionDTO
: DTO объект (содержит "Транзакциями" и Лимит) для работы с "Транзакциями" превысевшими свой "Лимит"
service:
- interface
TransactionService
: бизнес логика для работы с - classTransaction
- interface
LimitService
: бизнес логика для работы с classLimit
- interface
CurrencyService
: бизнес логика для работы с - classCurrency
(взаимодействие сcurrency-service
)
external:
- class
CurrencyServiceClient
: отвечает за взаимодействиеcurrency-service
- class
-
Реализация Features
-
1. Сохранение получаемой "Транзакций"
Для сохранения транзакции используется метод - save()
@Override public Transaction save(Transaction transaction) { TransactionServiceUtils.validateTransactionData(transaction); //... return repository.save(transaction); }
-
2. Реализовать логику разделение "Транзакций" по категориям расходов
За определение категории расходов для "Транзакций" отвечает - enum
ExpenseCategory
public enum ExpenseCategory { PRODUCT, SERVICE }
-
3. Реализовать логику установления "Лимита" по умолчанию
За определение Лимита по-умолчанию отвечает метод - setDefaultLimit()
Note: дата установления = 1st число месяца, в котором была совершена "Транзакций"
@Override public Limit setDefaultLimit(Transaction transaction) { Limit limit = new Limit(); limit.setId(0); limit.setLimit_currency_shortname("USD"); limit.setLimit_sum(BigDecimal.valueOf(1000.00)); limit.setLimit_datetime(ServiceUtils.getStartOfMonthDateTime(transaction.getDatetime())); limit.setExpense_category(transaction.getExpense_category()); return limit; }
-
4. Реализовать логику выставления флага
limit_exceeded
для "Транзакции"Для выставление флага
limit_exceeded
для "Транзакций" отвечает метод - checkTransactionForExceed()@Override public Transaction save(Transaction transaction) { //... transaction.setLimit_exceeded(checkTransactionForExceed(transaction, limit)); return repository.save(transaction); }
@Override public boolean checkTransactionForExceed(Transaction transaction, Limit limit) { /* Чтобы не сохранять конвертированный (в USD) вариант как сумму для текущей транзакции, * при расчете limitSumLeft передаем в нее копию */ Transaction transactionCopy = transaction.clone(); BigDecimal limitSumLeft = limitService.calculateLimitSumLeft(transactionCopy, limit); // Если limitSumLeft отрицательный или 0.0, то лимит был превышен if (limitSumLeft.compareTo(BigDecimal.ZERO) < 0) { return true; } return false; }
@Override public BigDecimal calculateLimitSumLeft(Transaction transaction, Limit limit) { ZonedDateTime limitStartDate = limit.getLimit_datetime(); ZonedDateTime transactionDateTime = transaction.getDatetime(); ExpenseCategory transactionCategory = transaction.getExpense_category(); // Обновляем лимит на начало месяца транзакции, если она произошла после установления лимита if (transactionDateTime.isAfter(limitStartDate)) { limitStartDate = ZonedDateTime.of(transactionDateTime.getYear(), transactionDateTime.getMonthValue(), 1, 0, 0, 0, 0, transactionDateTime.getZone()); } // Получаем все транзакции клиента за месяц List<Transaction> transactions = transactionService.getClientTransactionListForMonth(transactionDateTime); transactions.add(transaction); // Фильтруем транзакции, которые произошли после установления лимита в текущем месяце ZonedDateTime finalLimitStartDate = limitStartDate; List<Transaction> relevantTransactions = transactions.stream() .filter(t -> t.getDatetime().isAfter(finalLimitStartDate)) .filter(t -> t.getExpense_category() == transactionCategory) // Учитываем категорию расходов .map(t -> { Transaction transactionCopy = t.clone(); // Конвертируем сумму транзакции в USD transactionCopy.setSum(currencyService.convertToUSD(t.getCurrency_shortname(), t.getSum(), t.getDatetime())); // Конвертируем сумму транзакции в USD return transactionCopy; }) .collect(Collectors.toList()); // Вычисляем остаток лимита BigDecimal remainingLimit = limit.getLimit_sum(); for (Transaction t : relevantTransactions) { remainingLimit = remainingLimit.subtract(t.getSum()); } System.out.println("remainingLimit: " + remainingLimit); return remainingLimit; }
-
5. Реализовать логику конвертации валюты "Транзакции"
Для конвертации параметра "Транзакций"
sum
к 'USD' используется метод - convertToUSD()@Override public BigDecimal convertToUSD(String currency_shortname, BigDecimal transaction_sum, ZonedDateTime transaction_dateTime) { // Получаем список валют и их курсов (из API currency-service) List<Currency> currencyList = fetchCurrencyList(transaction_dateTime).block(); Map<String, BigDecimal> currencyMap = getListOfCurrency(currencyList); // Проверяем, есть ли указанная валюта в списке if (!currencyMap.containsKey(currency_shortname)) { return BigDecimal.ZERO; } // Получаем курс валюты к USD BigDecimal exchangeRate = currencyMap.get(currency_shortname); // Конвертируем сумму в USD по курсу return transaction_sum.divide(exchangeRate, 2, RoundingMode.HALF_UP); }
Где метод
fetchCurrencyList()
обращяется кcurrency-service
для получения списка валют на дату совершения "Транзакций", и методgetListOfCurrency()
обрабатывает этот список и возвращяет его в форматеMap<"Валюта","Курс к USD">
@Override public Map<String, BigDecimal> getListOfCurrency(List<Currency> currencyList) { Map<String, BigDecimal> currencyRatesMap = new HashMap<>(); // Добавляем все валюты и их курсы к USD из списка currencyList for (Currency currency : currencyList) { currencyRatesMap.put(currency.getCurrency_shortname(), currency.getRate_to_USD()); } return currencyRatesMap; }
-
6. Реализовать обращение к
currency-service
для получения списка валют на дату совершения "Транзакции"За обращение к
currency-service
отвечает - classCurrencyServiceClient
, который использует WebClient для выполненияHTTP-запросов
к сервису@Component public class CurrencyServiceClient { private final WebClient webClient; @Autowired public CurrencyServiceClient(WebClient.Builder webClientBuilder) { this.webClient = webClientBuilder .filter((request, next) -> { System.out.println("Request: " + request.method() + " " + request.url()); return next.exchange(request); }) .build(); } // http://localhost:8082/currency-service/api/currencies/{dateTime} public Mono<List<Currency>> getCurrencyList(ZonedDateTime dateTime) { return webClient.get() .uri(uriBuilder -> uriBuilder.scheme("http") .host("localhost") .port(8082) .path("/currency-service/api/currencies/" + dateTime) .build()) .retrieve() .bodyToFlux(Currency.class) // Преобразуем ответ в поток объектов Currency .collectList(); // Собираем объекты Currency в список } }
Для получения и обработки списка валют отвечает метод - fetchCurrencyList()
@Override public Mono<List<Currency>> fetchCurrencyList(ZonedDateTime transaction_dateTime) { List<Currency> currencyList = new ArrayList<>(); return currencyServiceClient.getCurrencyList(transaction_dateTime) .doOnNext(response -> { // Преобразование и добавление в currencyList List<Currency> returnedCurrencies = response.stream() .map(currency -> { Currency newCurrency = new Currency(); newCurrency.setCurrency_shortname(currency.getCurrency_shortname()); newCurrency.setRate_to_USD(currency.getRate_to_USD()); CurrencyRequest newCurrencyRequest = new CurrencyRequest(); newCurrencyRequest.setBase(currency.getCurrencyRequest().getBase()); newCurrencyRequest.setFormatted_timestamp(currency.getCurrencyRequest().getFormatted_timestamp()); newCurrency.setCurrencyRequest(newCurrencyRequest); return newCurrency; }) .collect(Collectors.toList()); currencyList.addAll(returnedCurrencies); }); }
-
7. Реализовать функцию установления "Лимита" Клиентом
За сохранение "Лимита" Клиента отвечает метод - setClientLimit()
@Override public Limit setClientLimit(Limit limit) { // Обновлять существующие лимиты запрещается checkLimitForExist(limit); /* автоматически выставляется текущая дата, * не позволяя выставить ее в прошедшем или будущем времени */ limit.setLimit_datetime(ServiceUtils.getCurrentDateTime()); // Лимит всегда USD limit.setLimit_currency_shortname("USD"); return repository.save(limit); }
После того как Клиент определит собственный "Лимит", перед выставлением флага
limit_exceeded
определяется актуальный "Лимит" для "Транзакций". Сначала проверяется есть ли в БД хотябы 1 "Лимит" удовлетворяющий параметруexpense_category
"Транзакций", если есть то он используется при вычсилении"Остатка месячного Лимита"
для "Транзакций", если его нет используется "Лимит" по-умолчанию (1000.00 USD)@Override public Transaction save(Transaction transaction) { //... Limit limit = new Limit(); if (limitService.hasRecords()) { Map<ExpenseCategory, Limit> latestLimits = limitService.getLatestLimitsForCategories(); // Проверяем, соответствует ли ExpenseCategory транзакции одной из категорий в возвращаемой Map Limit limitForTransactionCategory = latestLimits.get(transaction.getExpense_category()); if (limitForTransactionCategory != null) { /// Лимит Клиента BeanUtils.copyProperties(limitForTransactionCategory, limit); ServiceUtils.roundToHundredth(limit.getLimit_sum()); } else { // Лимит по умолчанию (1000.00) /* на тот случай если Клиент установил лимит * только для 1 из категорий ExpenseCategory */ limit = limitService.setDefaultLimit(transaction); } } else { // Лимит по умолчанию (1000.00) /* на тот случай если Клиент никогда не устанавливал своего лимита * т.е. для всех ExpenseCategory в месяце в котором была совершена транзакция * лимит = 1000.00 */ limit = limitService.setDefaultLimit(transaction); } //... return repository.save(transaction); }
-
8. Реализовать функцию получения "Транзакции", которые превысили свой "Лимит"
-
Реализация через SQL-запрос
SELECT t.id AS t_id, t.account_from, t.account_to, t.currency_shortname, t.sum, t.expense_category, t.datetime, t.limit_exceeded, CASE WHEN COALESCE(l.id, -1) = -1 THEN 0 ELSE l.id END AS limit_id, CASE WHEN COALESCE(l.id, 0) = 0 THEN 1000.00 ELSE l.limit_sum END AS limit_sum, CASE WHEN COALESCE(l.id, 0) = 0 THEN DATE_TRUNC('month', t.datetime) + INTERVAL '1 DAY' ELSE l.limit_datetime END AS limit_datetime, CASE WHEN COALESCE(l.id, 0) = 0 THEN 'USD' ELSE l.limit_currency_shortname END AS limit_currency_shortname, CASE WHEN COALESCE(l.id, 0) = 0 THEN t.expense_category ELSE COALESCE(l.expense_category, t.expense_category) END AS limit_expense_category FROM Transaction t LEFT JOIN ( SELECT t.id, MAX(l.limit_datetime) AS max_limit_datetime FROM Transaction t JOIN Limits l ON t.expense_category = l.expense_category AND t.datetime >= l.limit_datetime -- Фильтрация транзакции по (категории) + (времени) WHERE t.limit_exceeded = true GROUP BY t.id ) AS t_max_limit ON t.id = t_max_limit.id AND t.limit_exceeded = true LEFT JOIN Limits l ON t.expense_category = l.expense_category AND l.limit_datetime = t_max_limit.max_limit_datetime WHERE t.limit_exceeded = true -- Добавляем фильтрацию по limit_exceeded ORDER BY t.datetime DESC; -- Сортировка ответа, чтобы сначала были самые актуальные транзакции
@Override public List<ExceededTransactionDTO> getAllExceededTransactions_SQL() { String sqlQuery = " //... SQL-Запрос "; Query query = entityManager.createNativeQuery(sqlQuery); List<Object[]> resultList = query.getResultList(); List<ExceededTransactionDTO> exceededTransactionDTOs = new ArrayList<>(); for (Object[] result : resultList) { // Проверяем значение limit_exceeded if ((Boolean) result[7]) { Map<String, Object> transactionMap = new LinkedHashMap<>(); transactionMap.put("id", result[0]); transactionMap.put("account_from", result[1]); transactionMap.put("account_to", result[2]); transactionMap.put("currency_shortname", result[3]); transactionMap.put("sum", result[4]); transactionMap.put("expense_category", result[5]); transactionMap.put("datetime", result[6]); transactionMap.put("limit_exceeded", result[7]); ExceededTransactionDTO dto = new ExceededTransactionDTO(); Map<String, Object> limitMap = new LinkedHashMap<>(); if ((Long) result[8] != 0) { // Если есть Client limits limitMap.put("id", result[8]); limitMap.put("limit_sum", result[9]); limitMap.put("limit_datetime", result[10]); limitMap.put("limit_currency_shortname", result[11]); limitMap.put("expense_category", result[12]); dto.setLimit(LimitServiceUtils.convertMapToLimit(limitMap)); } else { // Если нет Client limits Limit defaultLimit = limitService.setDefaultLimit(TransactionServiceUtils.convertMapToTransaction(transactionMap)); limitMap.put("id", defaultLimit.getId()); limitMap.put("limit_sum", defaultLimit.getLimit_sum()); limitMap.put("limit_datetime", defaultLimit.getLimit_datetime()); limitMap.put("limit_currency_shortname", defaultLimit.getLimit_currency_shortname()); limitMap.put("expense_category", defaultLimit.getExpense_category()); dto.setLimit(defaultLimit); } dto.setTransaction(TransactionServiceUtils.convertMapToTransaction(transactionMap)); exceededTransactionDTOs.add(dto); } } return exceededTransactionDTOs; }
-
Реализация через Java-код
@Override public List<ExceededTransactionDTO> getAllExceededTransactions_Java() { // Получаем все транзакции, которые превысили лимиты List<Transaction> exceededTransactions = repository.findByLimit_exceededTrue(); // Создаем список DTO для превышенных транзакций List<ExceededTransactionDTO> exceededTransactionDTOs = new ArrayList<>(); // Проходим по каждой превышенной транзакции for (Transaction transaction : exceededTransactions) { // Получаем лимит для данной транзакции Limit limit = limitService.getLimitForTransaction(transaction); // Создаем DTO и устанавливаем транзакцию и соответствующий лимит ExceededTransactionDTO dto = new ExceededTransactionDTO(); dto.setTransaction(transaction); dto.setLimit(limit); // Добавляем DTO в список exceededTransactionDTOs.add(dto); } Collections.reverse(exceededTransactionDTOs); return exceededTransactionDTOs; }
-
-
currency-service
-
Основные Классы:
model:
- class
Currency
: объект для работы с "курсами валют" - class
CurrencyApiResponse
: объект для работыresponse
отВнешнего API
- class
CurrencyRequest
: объект для сохранения информации о прошлых запросах кВнешнему API
service:
- class
CurrencyService
: бизнес логика для работы с - classCurrency
, classCurrencyApiResponse
, classCurrencyRequest
external:
- class
OpenExchangeRatesClient
: отвечает за взаимодействие cВнешним API
- class
-
Реализация Features
-
1. Установить соединение с
Внешним API
За обеспечение соединения с
Внешним API
отвечает - classOpenExchangeRatesClient
, в которомapp_id
иbasePath
определен вconfig-server\src\main\resources\config\currency-service.yaml
@Component public class OpenExchangeRatesClient { private final WebClient webClient; @Value("${my.API_id}") private String app_id; @Value("${my.API_basePath}") private String basePath; @Autowired public OpenExchangeRatesClient(WebClient.Builder webClientBuilder) { this.webClient = webClientBuilder .filter((request, next) -> { System.out.println("Request: " + request.method() + " " + request.url()); return next.exchange(request); }) .build(); } // ... }
В этом классе метод - getCurrencyList_Latest() отвечает за получение списка "курсов валют" на текущий момент (up-to-date)
Note: метод обращяется к API
/latest.json
от https://openexchangerates.org/api// ... public Mono<CurrencyApiResponse> getCurrencyList_Latest() { return webClient.get() .uri(uriBuilder -> uriBuilder.scheme("https") .host(basePath) .path("/api/latest.json") .queryParam("app_id", app_id) .build()) .retrieve() .bodyToMono(CurrencyApiResponse.class); } // ...
и метод - getCurrencyList_Historical() отвечает за получение списка "курсов валют" на определенную дату в прошлом
Note: метод обращяется к API
/historical/*.json
от https://openexchangerates.org/api// ... public Mono<CurrencyApiResponse> getCurrencyList_Historical(String dateTime) { return webClient.get() .uri(uriBuilder -> uriBuilder.scheme("https") .host(basePath) .path("/api/historical/" + dateTime + ".json") .queryParam("app_id", app_id) .build()) .retrieve() .bodyToMono(CurrencyApiResponse.class); } // ...
-
2. Обработка полученных валют от
Внешнего API
За получение и обработку валют от
Внешнего API
отвечает метод - getCurrencyList()Note: в этом методе применяются принципы реактивного программирования от WebClient
@Override public Mono<List<Currency>> getCurrencyList(ZonedDateTime transaction_dateTime) { String currentDate_formatted = CurrencyServiceUtils.parseZoneDateTime(CurrencyServiceUtils.getCurrentDateTime()); String transactionDate_formatted = CurrencyServiceUtils.parseZoneDateTime(transaction_dateTime); LocalDate parsedCurrentDate = LocalDate.parse(currentDate_formatted); LocalDate parsedTransactionDate = LocalDate.parse(transactionDate_formatted); CurrencyRequest pastCurrencyRequest = requestRepository.findByFormatted_timestamp(transactionDate_formatted); if (pastCurrencyRequest != null) { System.out.println("Get data from local db!!! (currencyList at:" + pastCurrencyRequest.getFormatted_timestamp() + ")"); // Список валют взят из БД (запрос в openexchangerates.org/api/ НЕ делается) currencyList = currencyRepository.findAllByCurrencyRequestID(pastCurrencyRequest.getId()); return checkForUnavailableRate(transaction_dateTime, currencyList); } else { return Mono.defer(() -> { if (parsedTransactionDate.isEqual(parsedCurrentDate)) { return openExchangeRatesClient.getCurrencyList_Latest() .map(response -> { response.setTimestamp(currentDate_formatted); return response; }) .flatMap(response -> createCurrenciesFromResponse(Mono.just(response))) .flatMap(currencyList -> checkForUnavailableRate(transaction_dateTime, currencyList)); } else if (parsedTransactionDate.isBefore(parsedCurrentDate)) { return openExchangeRatesClient.getCurrencyList_Historical(transactionDate_formatted) .map(response -> { response.setTimestamp(transactionDate_formatted); return response; }) .flatMap(response -> createCurrenciesFromResponse(Mono.just(response))) .flatMap(currencyList -> checkForUnavailableRate(transaction_dateTime, currencyList)); } else { return Mono.error(new IllegalArgumentException("Transaction date cannot be in the future!!! (BACK-TO-THE-FUTURE)")); } }); } }
@Override public Mono<List<Currency>> createCurrenciesFromResponse(Mono<CurrencyApiResponse> responseMono) { return responseMono.flatMap(response -> { CurrencyRequest currencyRequest = getCurrencyRequest(response); requestRepository.save(currencyRequest); // Получаем список валют и сохраняем их в бд Map<String, BigDecimal> currencyRates = response.getRates(); currencyList = new ArrayList<>(); for (Map.Entry<String, BigDecimal> entry : currencyRates.entrySet()) { Currency currency = new Currency(); currency.setCurrency_shortname(entry.getKey()); currency.setRate_to_USD(entry.getValue()); currencyList.add(currency); } currencyList.forEach(currency -> currency.setCurrencyRequest(currencyRequest)); currencyRepository.saveAll(currencyList); return Mono.just(currencyList); }); }
@Override public CurrencyRequest getCurrencyRequest(CurrencyApiResponse response) { CurrencyRequest currencyRequest = new CurrencyRequest(); currencyRequest.setBase(response.getBase()); currencyRequest.setFormatted_timestamp(response.getTimestamp()); return currencyRequest; }
-
3. Реализовать логику получения "курсов валют" последнего закрытия
За определение категории расходов для "Транзакций" отвечает - enum
ExpenseCategory
/* это немного условная реализация требования из ТЗ (пункт 3) ("использовать данные последнего закрытия (previous_close)") * потому что у меня free-plan от https://openexchangerates.org/api/, * но в документации говорится что для '/historical/*.json' + '/latest.json' * в качестве rates фактический берутся данные последнего закрытия, * а значит что если запрос будет пустой, * то мы можем сделать еще один запрос на предыдущий день (чтобы получить previous_close rates) * '/latest.json'=(The latest rates will always be the most up-to-date data available on your plan) * '/historical/*.json'=(The historical rates returned are the last values we published for a given UTC day) */ @Override public Mono<List<Currency>> checkForUnavailableRate(ZonedDateTime transaction_dateTime, List<Currency> currencyList) { /* Если currencyList пустой, вызываем getCurrencyList на предыдущий день, * и так по рекурсии, пока не найдется доступный курс */ if (currencyList.isEmpty()) { ZonedDateTime previousDate = transaction_dateTime.minusDays(1); return getCurrencyList(previousDate); } else { // Если currencyList не пустой, просто возвращаем значение return Mono.just(currencyList); } }
-
Note
Если api-gateway
:
- запущен:
- port:
8060
(Общий для всех сервисов приложения)
- port:
- не запущен:
- port:
8081
(дляtransaction-service
) - port:
8082
(дляcurrency-service
)
- port:
API-запросы для - transaction-service
-
POST -> Для приема "Транзакций" (условно интеграция с банковскими сервисами)
[Request]:
http://localhost:8060/transaction-service/api/transactions
{ "account_from": "0000000123", "account_to": "9999999999", "currency_shortname": "USD", "sum": "500", "expense_category": "SERVICE" }
"currency_shortname":
KZT
,RUB
,USD
, ...
"expense_category":
SERVICE
/PRODUCT
[Response]:
Successfully saved
-
POST -> Для установки нового "Лимита" Клиентами
[Request]:
http://localhost:8060/transaction-service/api/client/limits
{ "limit_sum": "2000", "limit_currency_shortname": "USD", "expense_category": "SERVICE" }
"expense_category":
SERVICE
/PRODUCT
[Response]:
New limit Successfully saved
-
GET -> Для получения всех "Лимитов" Клиентами
[Request]:
http://localhost:8060/transaction-service/api/client/limits
[Response]:
[ { "id": 1, "limit_sum": 2000.0, "limit_datetime": "2024-04-01T00:00:00Z", "limit_currency_shortname": "USD", "expense_category": "SERVICE" }, ... // Получаем все установленные Лимиты Клиента ]
-
GET -> [SQL query] Для получение "Транзакций" превысивших "Лимит" (с указанием лимита, который был превышен)
[Request]:
http://localhost:8060/transaction-service/api/client/transactions/exceeded/sql-query
[Response]:
-
~ [DEFAULT Limit] (1000.00 USD):
[ { "transaction": { "id": 2, "account_from": 123, "account_to": 9999999999, "currency_shortname": "USD", "sum": 600.00, "expense_category": "SERVICE", "datetime": "2024-04-25T11:23:50.236144Z", "limit_exceeded": true }, "limit": { "id": 0, // Лимит по-умолчанию "limit_sum": 1000.0, "limit_datetime": "2024-04-01T00:00:00Z", "limit_currency_shortname": "USD", "expense_category": "SERVICE" } }, ... // Получаем все Транзакции превысившие свой Лимит ]
-
Client Limit:
[ { "transaction": { "id": 6, "account_from": 123, "account_to": 9999999999, "currency_shortname": "USD", "sum": 100.00, "expense_category": "SERVICE", "datetime": "2024-04-25T11:23:50.236144Z", "limit_exceeded": true }, "limit": { "id": 1, // Клиентский Лимит "limit_sum": 2000.0, "limit_datetime": "2024-04-01T00:00:00Z", "limit_currency_shortname": "USD", "expense_category": "SERVICE" } }, ... // Получаем все Транзакции превысившие свой Лимит ]
-
-
GET -> [Java code] Для получение "Транзакций" превысивших "Лимит" (с указанием лимита, который был превышен)
[Request]:
http://localhost:8060/transaction-service/api/client/transactions/exceeded/java-code
[Response]:
Структура ответа такая же как для
/exceeded/sql-query
, но Java реализация логики
API-запросы для - currency-service
-
GET -> Для получения "Курсов-валют" на определенную дату
[Request]:
http://localhost:8060/currency-service/api/currencies/2022-05-20T01:00:00+03:00
Note: Контроллер на стороне
currency-service
, принимает параметр типа ZoneDateTime -"2022-05-20T01:00:00+03:00"
отtransaction-service
, после чего дата будет приведена в формат"yyyy-mm-dd"
для соответствия API-запросам к https://openexchangerates.org/api/[Response]:
[ { "id": 1, "currency_shortname": "AED", "rate_to_USD": 3.67, "currencyRequest": { "id": 102, "base": "USD", "formatted_timestamp": "2022-05-20" } }, { "id": 2, "currency_shortname": "AFN", "rate_to_USD": 90.50, "currencyRequest": { "id": 102, "base": "USD", "formatted_timestamp": "2022-05-20" } }, ... // Получаем все курсы валют на - "2022-05-20T01:00:00+03:00" ]
-
GET -> Для получения списка запросов (совершенных к внешнему API)
[Request]:
http://localhost:8060/currency-service/api/currencies/requests
[Response]:
[ { "id": 1, "base": "USD", "formatted_timestamp": "2022-05-20" }, ... // Получаем все - предыдущие запросы к 'https://openexchangerates.org/api/' ]
-
GET -> Для получения всех "Курсов-валют" для ранее совершенных запросов
[Request]:
http://localhost:8060/currency-service/api/currencies
[Response]:
[ { "id": 1, "currency_shortname": "AED", "rate_to_USD": 3.67, "currencyRequest": { "id": 102, "base": "USD", "formatted_timestamp": "2022-05-20" } }, { "id": 2, "currency_shortname": "AFN", "rate_to_USD": 90.50, "currencyRequest": { "id": 102, "base": "USD", "formatted_timestamp": "2022-05-20" } }, ... // Получаем все курсы валют хранящиеся на локальном БД (предыдущие запросы к 'https://openexchangerates.org/api/') ]