JavaRush /وبلاگ جاوا /Random-FA /ایجاد یک سیستم نظارت بر قیمت بلیط هواپیما: راهنمای گام به...
Roman Beekeeper
مرحله

ایجاد یک سیستم نظارت بر قیمت بلیط هواپیما: راهنمای گام به گام [بخش اول]

در گروه منتشر شد

محتوا:

ایجاد یک سیستم نظارت بر قیمت بلیط هواپیما: راهنمای گام به گام [بخش 1] - 1سلام به همه، انجمن JavaRush! امروز در مورد نحوه نوشتن اپلیکیشن Spring Boot برای نظارت بر قیمت بلیط هواپیما به صورت گام به گام صحبت خواهیم کرد. این مقاله برای افرادی در نظر گرفته شده است که ایده ای در مورد زیر دارند:
  • REST و نحوه ایجاد نقاط پایانی REST.
  • پایگاه داده های رابطه ای؛
  • کار ماون (به ویژه وابستگی چیست)؛
  • شی JSON.
  • اصول ورود به سیستم
رفتار مورد انتظار:
  1. می توانید یک پرواز را برای یک تاریخ خاص انتخاب کنید و قیمت آن را پیگیری کنید. کاربر با آدرس ایمیل شناسایی می شود. به محض اینکه اشتراک در تغییر قیمت انجام شود، کاربر یک اعلان از طریق ایمیل دریافت می کند.
  2. هر 30 دقیقه (این فاصله از طریق application.properties پیکربندی می شود) حداقل قیمت یک پرواز برای همه اشتراک ها دوباره محاسبه می شود. اگر یکی از مقادیر کمتر شده باشد، کاربر از طریق ایمیل اعلان دریافت می کند.
  3. همه اشتراک‌هایی که تاریخ پروازشان قدیمی است حذف می‌شود.
  4. از طریق REST API می توانید:
    • ایجاد اشتراک؛
    • ویرایش؛
    • دریافت تمام اشتراک ها از طریق ایمیل؛
    • حذف اشتراک

برنامه عمل برای رسیدن به هدف

باید با این واقعیت شروع کنید که اطلاعات مربوط به پروازها باید از جایی گرفته شود. به طور معمول، وب سایت ها یک REST API باز ارائه می دهند که از طریق آن می توان اطلاعات را بازیابی کرد.

API (Application Programming Interface) رابطی است که از طریق آن می توانید با یک برنامه تعامل داشته باشید. از این طریق می توانیم پلی بسازیم تا REST API چیست.

REST API یک رابط از درخواست های REST است که می تواند برای ارتباط با یک برنامه وب استفاده شود.

برای انجام این کار، از Skyscanner یا بهتر است بگوییم، API (در وب سایت Rakuten API ) استفاده می کنیم . بعد، شما باید چارچوب مناسب را به عنوان پایه اصلی انتخاب کنید. محبوب ترین و مورد تقاضا اکوسیستم Spring و تاج ایجاد آنها - Spring Boot است. می‌توانید به وب‌سایت رسمی آنها بروید یا می‌توانید مقاله Habré را بخوانید . برای ذخیره اشتراک های کاربران از پایگاه داده داخلی H2 استفاده می کنیم . برای خواندن از JSON به کلاس‌ها و برعکس، از پروژه جکسون استفاده می‌کنیم ( اینجا لینک منبع ما است ). ما از Spring-boot-Starter-mail برای ارسال پیام به کاربران استفاده خواهیم کرد . برای اینکه برنامه بتواند حداقل قیمت را در یک فرکانس مشخص دوباره محاسبه کند، از Spring Scheduler استفاده می کنیم . برای ایجاد یک REST API از Spring-boot-Starter-Web استفاده می کنیم . برای اینکه کدهای قرض گرفته شده را ننویسیم (گیرنده ها، تنظیم کننده ها، مساوی های باطل و هش کد، toString() برای اشیا)، از Project Lombok استفاده می کنیم . برای احساس و دیدن REST API، ما از Swagger 2 و بلافاصله Swagger UI (رابط کاربری) برای ردیابی بلادرنگ استفاده خواهیم کرد. اکنون به نظر می رسد: ایجاد یک سیستم نظارت بر قیمت بلیط هواپیما: راهنمای گام به گام [بخش 1] - 2که در آن 4 سؤال باقی مانده که مربوط به ایجاد، ویرایش، دریافت و حذف اشتراک هستند.

کاوش در Skyscanner API

بیایید پیوند به api rakuten را دنبال کنیم . ابتدا باید ثبت نام کنید. ایجاد یک سیستم نظارت بر قیمت بلیط هواپیما: راهنمای گام به گام [بخش 1] - 3همه اینها برای دریافت یک کلید منحصر به فرد برای استفاده از سایت آنها و درخواست به APIهای عمومی که در آن پست شده است، لازم است. یکی از این APIها جستجوی پرواز Skyscanner است که ما به آن نیاز داریم . حالا بیایید بفهمیم که چگونه کار می کند. بیایید درخواست GET List Places را پیدا کنیم. تصویر نشان می دهد که باید داده ها را پر کنید و Test Endpoint را شروع کنید ، در نتیجه ما پاسخی را به شکل یک شی JSON در سمت راست دریافت می کنیم: ایجاد سیستمی برای نظارت بر قیمت بلیط هواپیما: راهنمای گام به گام [قسمت 1] - 4و درخواست به این صورت ایجاد می شود:

https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/{country}/{currency}/{locale}/?query={query}
و همه پارامترها در این فرمول جایگزین می شوند، دریافت می کنیم:

https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com/apiservices/autosuggest/v1.0/UK/GBP/en-GB/?query=Stockholm
و دو سربرگ به این درخواست ها ارسال می شود:

.header("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com")
.header("x-rapidapi-key", "sing-up-for-key"),
جایی sign-up-for-keyکه پس از ثبت صادر می شود. برای پیگیری کاهش قیمت، به نقطه پایانی Browse Quotes نیاز داریم . خودت پیداش کن :)

ایجاد یک فریم ورک اپلیکیشن بر اساس Spring Boot

برای ایجاد سریع و آسان یک پروژه با Spring Boot، می توانید از Spring Initializr استفاده کنید . گزینه های زیر را انتخاب کنید:
  1. پروژه Maven
  2. جاوا
  3. 2.1.10
  4. گروه - هر کدام که لازم است، مثلا ru.javarush
  5. مصنوع - دقیقاً یکسان است، به عنوان مثال نظارت بر پروازها
  6. در جستجوی وابستگی به دنبال موارد زیر هستیم:
    • وب بهار
    • فرستنده ایمیل جاوا
    • بهار داده Jpa
    • پایگاه داده H2
و سپس روی Generate کلیک کنید . تمام: پروژه تمام شده به عنوان یک بایگانی بارگیری می شود. اگر چیزی درست نشد، می توانید از پیوندی که پروژه مورد نظر را در آن ذخیره کرده ام استفاده کنید . البته، بهتر است این کار را خودتان انجام دهید و نحوه کار را درک کنید. برنامه شامل سه لایه خواهد بود:
  • CONTROLLER - وارد برنامه شوید. REST API در اینجا توضیح داده خواهد شد
  • SERVICE یک لایه منطق تجاری است. کل منطق برنامه در اینجا توضیح داده خواهد شد.
  • REPOSITORY - لایه ای برای کار با پایگاه داده.
همچنین کلاس های مربوط به کلاینت برای Skyscanner Flight Search API یک بسته جداگانه خواهد بود.

ما در حال نوشتن یک مشتری برای درخواست به Skyscanner Flight Search API در پروژه هستیم

Skyscanner با مهربانی مقاله ای در مورد نحوه استفاده از API آنها ارائه کرده است (ما جلسه ای با درخواست فعال ایجاد نمی کنیم). منظور از "نوشتن مشتری" چیست؟ ما باید یک درخواست برای یک URL خاص با پارامترهای خاص ایجاد کنیم و یک DTO (شیء انتقال داده) برای داده هایی که به ما بازگردانده شده است آماده کنیم. چهار گروه درخواست در سایت وجود دارد:
  1. جستجوی پرواز زنده - در حال حاضر آن را غیر ضروری در نظر نخواهیم گرفت.
  2. مکان ها - بیایید بنویسیم.
  3. قیمت‌های پرواز را مرور کنید - ما از یک درخواست استفاده می‌کنیم که در آن می‌توانید تمام اطلاعات را دریافت کنید.
  4. محلی سازی - بیایید آن را اضافه کنیم تا بدانیم از چه داده هایی پشتیبانی می شود.

یک سرویس مشتری برای درخواست محلی سازی ایجاد کنید:

این طرح به سادگی یک شلغم بخارپز است: یک درخواست ایجاد کنید، به پارامترها نگاه کنید، به پاسخ نگاه کنید. دو پرس و جو وجود دارد: نشانگرهای فهرست و ارزها. بیایید با ارزها شروع کنیم. شکل نشان می دهد که این یک درخواست بدون فیلدهای اضافی است: برای دریافت اطلاعات در مورد ارزهای پشتیبانی شده لازم است: ایجاد سیستم نظارت بر قیمت بلیط هواپیما: راهنمای گام به گام [قسمت 1] - 6پاسخ به شکل یک شی JSON است که شامل مجموعه ای از همان اشیاء است، به عنوان مثال:
{
"Code":"LYD"
"Symbol":"د.ل.‏"
"ThousandsSeparator":","
"DecimalSeparator":"."
"SymbolOnLeft":true
"SpaceBetweenAmountAndSymbol":false
"RoundingCoefficient":0
"DecimalDigits":3
}
بیایید یک CurrencyDto برای این شی ایجاد کنیم:
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

/**
* Data transfer object for Currency.
*/
@Data
public class CurrencyDto {

   @JsonProperty("Code")
   private String code;

   @JsonProperty("Symbol")
   private String symbol;

   @JsonProperty("ThousandsSeparator")
   private String thousandsSeparator;

   @JsonProperty("DecimalSeparator")
   private String decimalSeparator;

   @JsonProperty("SymbolOnLeft")
   private boolean symbolOnLeft;

   @JsonProperty("SpaceBetweenAmountAndSymbol")
   private boolean spaceBetweenAmountAndSymbol;

   @JsonProperty("RoundingCoefficient")
   private int roundingCoefficient;

   @JsonProperty("DecimalDigits")
   private int decimalDigits;
}
جایی که:
  • @Data یک حاشیه نویسی از پروژه Lombok است و همه متدهای getter، setter، override toString()،quals() و hashCode() را تولید می کند. چه چیزی خوانایی کد را بهبود می بخشد و زمان نوشتن اشیاء POJO را سرعت می بخشد.
  • @JsonProperty ("کد") حاشیه نویسی از پروژه جکسون است که می گوید چه فیلدی به این متغیر اختصاص داده می شود. یعنی یک فیلد در JSON برابر با Code به متغیر کد اختصاص داده می شود .
مقاله رسمی Skyscanner استفاده از کتابخانه UniRest را برای درخواست های REST پیشنهاد می کند . بنابراین، ما یک سرویس دیگر می نویسیم که درخواست ها را از طریق REST پیاده سازی می کند. این UniRestService خواهد بود . برای انجام این کار، یک وابستگی جدید به maven اضافه کنید:
<dependency>
  <groupId>com.mashape.unirest</groupId>
  <artifactId>unirest-java</artifactId>
  <version>1.4.9</version>
</dependency>
در مرحله بعد، ما یک سرویس می نویسیم که درخواست های REST را انجام می دهد. البته، برای هر مشتری/سرویس یک رابط و پیاده سازی آن ایجاد خواهیم کرد:
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;

/**
* Service, which is manipulating with Rest calls.
*/
public interface UniRestService {

   /**
   * Create GET request based on provided {@param path} with needed headers.
   *
   * @param path provided path with all the needed data
   * @return {@link HttpResponse<jsonnode>} response object.
   */
   HttpResponse<jsonnode> get(String path);

}
و اجرای آن:
import com.github.romankh3.flightsmonitoring.exception.FlightClientException;
import com.github.romankh3.flightsmonitoring.client.service.UniRestService;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

/**
* {@inheritDoc}
*/
@Slf4j
@Service
public class UniRestServiceImpl implements UniRestService {

   public static final String HOST = "https://skyscanner-skyscanner-flight-search-v1.p.rapidapi.com";

   public static final String PLACES_FORMAT = "/apiservices/autosuggest/v1.0/%s/%s/%s/?query=%s";
   public static final String CURRENCIES_FORMAT = "/apiservices/reference/v1.0/currencies";
   public static final String COUNTRIES_FORMAT = "/apiservices/reference/v1.0/countries/%s";

   public static final String PLACES_KEY = "Places";
   public static final String CURRENCIES_KEY = "Currencies";
   public static final String COUNTRIES_KEY = "Countries";

   @Value("${x.rapid.api.key}")
   private String xRapidApiKey;

   /**
    * {@inheritDoc}
    */
   @Override
   public HttpResponse<jsonnode> get(String path) {
       HttpResponse<jsonnode> response = null;
       try {
           response = Unirest.get(HOST + path)
                   .header("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com")
                   .header("x-rapidapi-key", xRapidApiKey)
                   .asJson();
       } catch (UnirestException e) {
           throw new FlightClientException(String.format("Request failed, path=%s", HOST + path), e);
       }

       log.info("Response from Get request, on path={}, statusCode={}, response={}", path, response.getStatus(), response.getBody().toString());
       return response;
   }
}
ماهیت آن این است که تمام درخواست هایی که ما به آنها علاقه مندیم برای درخواست های GET ایجاد می شوند و این سرویس یک درخواست آماده را می پذیرد و هدرهای لازم مانند:
.header("x-rapidapi-host", "skyscanner-skyscanner-flight-search-v1.p.rapidapi.com")
.header("x-rapidapi-key", xRapidApiKey)
برای گرفتن داده از ویژگی ها، از حاشیه نویسی @Value مانند شکل زیر استفاده کنید:
@Value("${x.rapid.api.key}")
private String xRapidApiKey;
می گوید که در application.properties خاصیتی به نام x.rapid.api.key وجود خواهد داشت که باید به این متغیر تزریق شود. ما از شر مقادیر کدگذاری شده خلاص می شویم و تعریف این متغیر را از کد برنامه استخراج می کنیم. علاوه بر این، وقتی این برنامه را در GitHub منتشر می کنم، ارزش این ویژگی را اضافه نمی کنم. این کار به دلایل امنیتی انجام می شود. ما سرویسی نوشته‌ایم که با درخواست‌های REST کار می‌کند، اکنون زمان سرویسی برای محلی‌سازی است. ما در حال ساخت یک برنامه بر اساس OOP هستیم، بنابراین رابط LocalizationClient و پیاده سازی آن را LocalisationClientImpl ایجاد می کنیم :
import com.github.romankh3.flightsmonitoring.client.dto.CountryDto;
import com.github.romankh3.flightsmonitoring.client.dto.CurrencyDto;
import java.io.IOException;
import java.util.List;

/**
* Client for SkyScanner localisation.
*/
public interface LocalisationClient {

   /**
    * Retrieve the market countries that SkyScanner flight search API support. Most suppliers (airlines,
    * travel agents and car hire dealers) set their fares based on the market (or country of purchase).
    * It is therefore necessary to specify the market country in every query.
    *
    * @param locale locale of the response.
    *
    * @return the collection of the {@link CountryDto} objects.
    *
    * @throws IOException
    */
   List<CountryDto> retrieveCountries(String locale);

   /**
    * Retrieve the currencies that we ScyScanner flight search API.
    *
    * @return the collection of the {@link CurrencyDto} objects.
    *
    * @throws IOException
    */
   List<CurrencyDto> retrieveCurrencies();

}
و پیاده سازی LocalisationClientImpl
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.COUNTRIES_FORMAT;
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.COUNTRIES_KEY;
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.CURRENCIES_FORMAT;
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.CURRENCIES_KEY;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.romankh3.flightsmonitoring.client.dto.CountryDto;
import com.github.romankh3.flightsmonitoring.client.dto.CurrencyDto;
import com.github.romankh3.flightsmonitoring.client.service.LocalisationClient;
import com.github.romankh3.flightsmonitoring.client.service.UniRestService;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.exceptions.UnirestException;
import java.io.IOException;
import java.util.List;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
* {@inheritDoc}
*/
@Component
public class LocalisationClientImpl implements LocalisationClient {

   @Autowired
   private UniRestService uniRestService;

   @Autowired
   private ObjectMapper objectMapper;

   /**
    * {@inheritDoc}
    */
   @Override
   public List<CountryDto> retrieveCountries(String locale) throws IOException {
       HttpResponse<JsonNode> response = uniRestService.get(String.format(COUNTRIES_FORMAT, locale));

       if (response.getStatus() != HttpStatus.SC_OK) {
           return null;
       }

       String jsonList = response.getBody().getObject().get(COUNTRIES_KEY).toString();

       return objectMapper.readValue(jsonList, new TypeReference<List<CountryDto>>() {
       });
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public List<CurrencyDto> retrieveCurrencies() throws IOException, UnirestException {

       HttpResponse<JsonNode> response = uniRestService.get(CURRENCIES_FORMAT);
       if (response.getStatus() != HttpStatus.SC_OK) {
           return null;
       }

       String jsonList = response.getBody().getObject().get(CURRENCIES_KEY).toString();

       return objectMapper.readValue(jsonList, new TypeReference<List<CurrencyDto>>() {
       });
   }
}
جایی که
  • @Autowired یک حاشیه نویسی است که می گوید شما باید یک شی را به این کلاس تزریق کنید و بدون ایجاد آن، یعنی بدون عملیات Object جدید، از آن استفاده کنید.
  • @Component یک حاشیه‌نویسی است که می‌گوید این شی باید به Application Context اضافه شود تا بعداً بتوان آن را با استفاده از حاشیه‌نویسی @Autowired تزریق کرد.
  • ObjectMapper objectMapper یک شی از پروژه جکسون است که همه اینها را به اشیاء جاوا ترجمه می کند.
  • ارزDTO و CountryDto:
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

/**
* Data transfer object for Currency.
*/
@Data
public class CurrencyDto {

   @JsonProperty("Code")
   private String code;

   @JsonProperty("Symbol")
   private String symbol;

   @JsonProperty("ThousandsSeparator")
   private String thousandsSeparator;

   @JsonProperty("DecimalSeparator")
   private String decimalSeparator;

   @JsonProperty("SymbolOnLeft")
   private boolean symbolOnLeft;

   @JsonProperty("SpaceBetweenAmountAndSymbol")
   private boolean spaceBetweenAmountAndSymbol;

   @JsonProperty("RoundingCoefficient")
   private int roundingCoefficient;

   @JsonProperty("DecimalDigits")
   private int decimalDigits;
}
	и
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

/**
* Data transfer object for Country.
*/
@Data
public class CountryDto {

   @JsonProperty("Code")
   private String code;

   @JsonProperty("Name")
   private String name;
}
برای تزریق یک ObjectMapper به هر بخشی از پروژه، ایجاد و اضافه کردن آن به ApplicationContext را از طریق یک کلاس پیکربندی اضافه کردم.
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* {@link Configuration} class.
*/
@Configuration
public class Config {

   @Bean
   public ObjectMapper objectMapper() {
       ObjectMapper objectMapper = new ObjectMapper();
       objectMapper.registerModule(new JavaTimeModule());
       return objectMapper;
   }
}
حاشیه‌نویسی @Configuration به Spring می‌گوید که تنظیماتی در این کلاس وجود خواهد داشت. و فقط برای این موضوع ObjectMapper را اضافه کردم. به طور مشابه، PlacesClient و PlacesClientImpl را اضافه می کنیم:
import com.github.romankh3.flightsmonitoring.client.dto.PlaceDto;
import com.github.romankh3.flightsmonitoring.client.dto.PlacesDto;
import com.mashape.unirest.http.exceptions.UnirestException;
import java.io.IOException;
import java.util.List;

/**
* SkyScanner client.
*/
public interface PlacesClient {

   /**
    * Get a list of places that match a query string based on arguments.
    *
    * @param query the code of the city.
    * @param country the code of the country.
    * @param currency the code of the currency.
    * @param locale the code of the locale.
    * @return the collection of the {@link PlaceDto} objects.
    */
   List<PlacesDto> retrieveListPlaces(String query, String country, String currency, String locale)
           throws IOException, UnirestException;
}
و
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.PLACES_FORMAT;
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.PLACES_KEY;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.romankh3.flightsmonitoring.client.dto.PlacesDto;
import com.github.romankh3.flightsmonitoring.client.service.PlacesClient;
import com.github.romankh3.flightsmonitoring.client.service.UniRestService;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.exceptions.UnirestException;
import java.io.IOException;
import java.util.List;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* {@inheritDoc}
*/
@Service
public class PlacesClientImpl implements PlacesClient {

   @Autowired
   private UniRestService uniRestService;

   @Autowired
   private ObjectMapper objectMapper;


   /**
    * {@inheritDoc}
    */
   @Override
   public List<PlacesDto> retrieveListPlaces(String query, String country, String currency, String locale)
           throws IOException, UnirestException {
       HttpResponse<JsonNode> response = uniRestService
               .get(String.format(PLACES_FORMAT, country, currency, locale, query));

       if (response.getStatus() != HttpStatus.SC_OK) {
           return null;
       }

       String jsonList = response.getBody().getObject().get(PLACES_KEY).toString();

       return objectMapper.readValue(jsonList, new TypeReference<List<PlacesDto>>() {
       });
   }
}
جایی که PlacesDto به شکل زیر است:
import com.fasterxml.jackson.annotation.JsonProperty;
import com.github.romankh3.flightsmonitoring.client.service.PlacesClient;
import lombok.Data;

/**
* Using for {@link PlacesClient}.
*/
@Data
public class PlacesDto {

   @JsonProperty("PlaceId")
   private String placeId;

   @JsonProperty("PlaceName")
   private String placeName;

   @JsonProperty("CountryId")
   private String countryId;

   @JsonProperty("RegionId")
   private String regionId;

   @JsonProperty("CityId")
   private String cityId;

   @JsonProperty("CountryName")
   private String countryName;
}
و در نهایت، خدمات مشتری که بر اساس داده های لازم، حداقل قیمت یک پرواز و تمام اطلاعات لازم را برمی گرداند: FlightPriceClient و FlightPriceClientImpl. ما فقط یک درخواست را اجرا خواهیم کرد، browseQuotes. FlightPriceClient:
import com.github.romankh3.flightsmonitoring.client.dto.FlightPricesDto;

/**
* Browse flight prices.
*/
public interface FlightPricesClient {

   /**
    * Browse quotes for current flight based on provided arguments. One-way ticket.
    *
    * @param country the country from
    * @param currency the currency to get price
    * @param locale locale for the response
    * @param originPlace origin place
    * @param destinationPlace destination place
    * @param outboundPartialDate outbound date
    * @return {@link FlightPricesDto} object.
    */
   FlightPricesDto browseQuotes(String country, String currency, String locale, String originPlace,
           String destinationPlace, String outboundPartialDate);

   /**
    * Browse quotes for current flight based on provided arguments. Round trip ticket.
    *
    * @param country the country from
    * @param currency the currency to get price
    * @param locale locale for the response
    * @param originPlace origin place
    * @param destinationPlace destination place
    * @param outboundPartialDate outbound date
    * @param inboundPartialDate inbound date
    * @return {@link FlightPricesDto} object.
    */
   FlightPricesDto browseQuotes(String country, String currency, String locale, String originPlace,
           String destinationPlace, String outboundPartialDate, String inboundPartialDate);
}
FlightPriceClientImpl
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.CURRENCIES_KEY;
import static com.github.romankh3.flightsmonitoring.client.service.impl.UniRestServiceImpl.PLACES_KEY;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.romankh3.flightsmonitoring.client.dto.CarrierDto;
import com.github.romankh3.flightsmonitoring.client.dto.CurrencyDto;
import com.github.romankh3.flightsmonitoring.client.dto.FlightPricesDto;
import com.github.romankh3.flightsmonitoring.client.dto.PlaceDto;
import com.github.romankh3.flightsmonitoring.client.dto.QuoteDto;
import com.github.romankh3.flightsmonitoring.client.dto.ValidationErrorDto;
import com.github.romankh3.flightsmonitoring.client.service.FlightPricesClient;
import com.github.romankh3.flightsmonitoring.client.service.UniRestService;
import com.github.romankh3.flightsmonitoring.exception.FlightClientException;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import java.io.IOException;
import java.util.List;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* {@inheritDoc}
*/
@Service
public class FlightPricesClientImpl implements FlightPricesClient {

   public static final String BROWSE_QUOTES_FORMAT = "/apiservices/browsequotes/v1.0/%s/%s/%s/%s/%s/%s";
   public static final String OPTIONAL_BROWSE_QUOTES_FORMAT = BROWSE_QUOTES_FORMAT + "?inboundpartialdate=%s";

   public static final String QUOTES_KEY = "Quotes";
   public static final String ROUTES_KEY = "Routes";
   public static final String DATES_KEY = "Dates";
   public static final String CARRIERS_KEY = "Carriers";
   public static final String VALIDATIONS_KEY = "ValidationErrors";

   @Autowired
   private UniRestService uniRestService;

   @Autowired
   private ObjectMapper objectMapper;

   /**
    * {@inheritDoc}
    */
   @Override
   public FlightPricesDto browseQuotes(String country, String currency, String locale, String originPlace,
           String destinationPlace, String outboundPartialDate) {

       HttpResponse<JsonNode> response = uniRestService.get(String
               .format(BROWSE_QUOTES_FORMAT, country, currency, locale, originPlace, destinationPlace,
                       outboundPartialDate));
       return mapToObject(response);
   }

   public FlightPricesDto browseQuotes(String country, String currency, String locale, String originPlace,
           String destinationPlace, String outboundPartialDate, String inboundPartialDate) {
       HttpResponse<JsonNode> response = uniRestService.get(String
               .format(OPTIONAL_BROWSE_QUOTES_FORMAT, country, currency, locale, originPlace, destinationPlace,
                       outboundPartialDate, inboundPartialDate));
       return mapToObject(response);
   }

   private FlightPricesDto mapToObject(HttpResponse<JsonNode> response) {
       if (response.getStatus() == HttpStatus.SC_OK) {
           FlightPricesDto flightPricesDto = new FlightPricesDto();
           flightPricesDto.setQuotas(readValue(response.getBody().getObject().get(QUOTES_KEY).toString(),
                   new TypeReference<List<QuoteDto>>() {
                   }));
           flightPricesDto.setCarriers(readValue(response.getBody().getObject().get(CARRIERS_KEY).toString(),
                   new TypeReference<List<CarrierDto>>() {
                   }));
           flightPricesDto.setCurrencies(readValue(response.getBody().getObject().get(CURRENCIES_KEY).toString(),
                   new TypeReference<List<CurrencyDto>>() {
                   }));
           flightPricesDto.setPlaces(readValue(response.getBody().getObject().get(PLACES_KEY).toString(),
                   new TypeReference<List<PlaceDto>>() {
                   }));
           return flightPricesDto;
       }
       throw new FlightClientException(String.format("There are validation errors. statusCode = %s", response.getStatus()),
               readValue(response.getBody().getObject().get(VALIDATIONS_KEY).toString(),
                       new TypeReference<List<ValidationErrorDto>>() {
                       }));
   }

   private <T> List<T> readValue(String resultAsString, TypeReference<List<T>> valueTypeRef) {
       List<T> list;
       try {
           list = objectMapper.readValue(resultAsString, valueTypeRef);
       } catch (IOException e) {
           throw new FlightClientException("Object Mapping failure.", e);
       }
       return list;
   }
}
جایی که FlightClientException به نظر می رسد:
import com.github.romankh3.flightsmonitoring.client.dto.ValidationErrorDto;
import java.util.List;

/**
* A {@link RuntimeException} that is thrown in case of an flight monitoring failures.
*/
public final class FlightClientException extends RuntimeException {

   public FlightClientException(String message) {
       super(message);
   }

   public FlightClientException(String message, Throwable throwable) {
       super(message, throwable);
   }

   public FlightClientException(String message, List<ValidationErrorDto> errors) {
       super(message);
       this.validationErrorDtos = errors;
   }

   private List<ValidationErrorDto> validationErrorDtos;
}
در نتیجه، طبق داده های PlacesCl
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION