JavaRush /مدونة جافا /Random-AR /إنشاء نظام لمراقبة أسعار تذاكر الطيران: دليل خطوة بخطوة [...
Roman Beekeeper
مستوى

إنشاء نظام لمراقبة أسعار تذاكر الطيران: دليل خطوة بخطوة [الجزء الأول]

نشرت في المجموعة

محتوى:

إنشاء نظام لمراقبة أسعار تذاكر الطيران: دليل خطوة بخطوة [الجزء الأول] - 1مرحبًا بالجميع، مجتمع JavaRush! سنتحدث اليوم عن كيفية كتابة تطبيق Spring Boot لمراقبة أسعار تذاكر الطيران خطوة بخطوة. المقالة مخصصة للأشخاص الذين لديهم فكرة عن:
  • REST وكيفية بناء نقاط نهاية REST؛
  • قواعد البيانات العلائقية؛
  • عمل مخضرم (على وجه الخصوص، ما هو التبعية)؛
  • كائن JSON؛
  • مبادئ التسجيل.
سلوك متوقع:
  1. يمكنك اختيار رحلة بتاريخ محدد وتتبع سعرها. يتم التعرف على المستخدم عن طريق عنوان البريد الإلكتروني. بمجرد الاشتراك في تغيير السعر، يتلقى المستخدم إشعارًا عبر البريد الإلكتروني.
  2. كل 30 دقيقة (يتم تكوين هذا الفاصل الزمني من خلال application.properties) تتم إعادة حساب الحد الأدنى لسعر الرحلة لجميع الاشتراكات. إذا أصبحت إحدى القيم أقل، فسيتلقى المستخدم إشعارًا عبر البريد الإلكتروني.
  3. سيتم حذف جميع الاشتراكات ذات تاريخ الرحلة القديم.
  4. عبر REST API يمكنك:
    • إنشاء اشتراك؛
    • يحرر؛
    • تلقي كافة الاشتراكات عن طريق البريد الإلكتروني.
    • حذف الاشتراك.

خطة العمل لتحقيق الهدف

عليك أن تبدأ بحقيقة أن المعلومات المتعلقة بالرحلات الجوية يجب أن تؤخذ من مكان ما. عادةً ما توفر مواقع الويب واجهة REST API مفتوحة يمكن من خلالها استرجاع المعلومات.

API (واجهة برمجة التطبيقات) هي واجهة يمكنك من خلالها التفاعل مع التطبيق. من هذا يمكننا بناء جسر لماهية REST API.

REST API هي واجهة لطلبات REST التي يمكن استخدامها للتواصل مع تطبيق الويب.

للقيام بذلك، سوف نستخدم Skyscanner ، أو بالأحرى، API (على موقع Rakuten API ). بعد ذلك، تحتاج إلى اختيار الإطار المناسب كأساس أساسي. الأكثر شعبية والطلب هو النظام البيئي الربيع وتاج خلقهم - Spring Boot. يمكنك الذهاب إلى موقعهم الرسمي على الإنترنت، أو يمكنك قراءة المقال على حبري . لتخزين اشتراكات المستخدم سوف نستخدم قاعدة بيانات H2 المدمجة . للقراءة من JSON إلى الفصول الدراسية والعودة، سنستخدم مشروع Jackson ( هذا هو الرابط الموجود على موردنا ). سنستخدم Spring-boot-starter-mail لإرسال رسائل إلى المستخدمين ، ولكي يتمكن التطبيق من إعادة حساب الحد الأدنى للسعر عند تردد معين، سنستخدم Spring Scholer . لإنشاء REST API سنستخدم Spring-boot-starter-web . لكي لا نكتب تعليمات برمجية مستعارة (getters، setters، override Equis و hashcode، toString() للكائنات)، سنستخدم Project Lombok . للشعور بواجهة برمجة تطبيقات REST ورؤيتها، سنستخدم Swagger 2 وعلى الفور Swagger UI (واجهة المستخدم) للتتبع في الوقت الفعلي. إليك ما يبدو عليه الآن: إنشاء نظام لمراقبة أسعار تذاكر الطيران: دليل خطوة بخطوة [الجزء 1] - 2حيث توجد 4 استعلامات بقية تتوافق مع إنشاء الاشتراكات وتحريرها والحصول عليها وحذفها.

استكشاف Skyscanner API

دعنا نتبع الرابط إلى rakuten api . تحتاج أولاً إلى التسجيل، إنشاء نظام لمراقبة أسعار تذاكر الطيران: دليل خطوة بخطوة [الجزء 1] - 3كل هذا مطلوب للحصول على مفتاح فريد لاستخدام موقعهم وتقديم طلبات إلى واجهات برمجة التطبيقات العامة المنشورة عليه. إحدى واجهات برمجة التطبيقات هذه هي Skyscanner Flight Search التي نحتاجها . الآن دعونا معرفة كيف يعمل. فلنبحث عن طلب GET List Places. توضح الصورة أنك بحاجة إلى ملء البيانات وبدء اختبار نقطة النهاية ، ونتيجة لذلك نتلقى استجابة على شكل كائن JSON على اليمين: إنشاء نظام لمراقبة أسعار تذاكر الطيران: دليل خطوة بخطوة [الجزء الأول] - 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يتم إصداره بعد التسجيل. لتتبع انخفاض الأسعار، سنحتاج إلى نقطة نهاية تصفح الأسعار . ابحث عنها بنفسك :)

إنشاء إطار تطبيق يعتمد على Spring Boot

لإنشاء مشروع بسرعة وسهولة باستخدام Spring Boot، يمكنك استخدام Spring Initializr . حدد الخيارات التالية:
  1. مشروع مافن
  2. جافا
  3. 2.1.10
  4. المجموعة - كل ما تعتقد أنه ضروري، على سبيل المثال ru.javarush
  5. قطعة أثرية - بالضبط نفس الشيء، على سبيل المثال مراقبة الرحلات الجوية
  6. في بحث التبعية نبحث عن ما يلي:
    • ويب الربيع
    • جافا بريد المرسل
    • بيانات الربيع Jpa
    • قاعدة بيانات H2
ومن ثم انقر فوق إنشاء . هذا كل شيء: سيتم تنزيل المشروع النهائي كأرشيف. إذا لم ينجح شيء ما، يمكنك استخدام الرابط الذي قمت بحفظ المشروع المطلوب فيه . بالطبع، من الأفضل أن تفعل ذلك بنفسك وأن تفهم كيف يعمل. سيتكون التطبيق من ثلاث طبقات:
  • وحدة التحكم - تسجيل الدخول إلى التطبيق. سيتم وصف REST API هنا
  • الخدمة هي طبقة منطق الأعمال. سيتم وصف المنطق الكامل للتطبيق هنا.
  • المستودع - طبقة للعمل مع قاعدة البيانات.
وأيضًا، ستكون الفئات المرتبطة بعميل Skyscanner Flight Search API بمثابة حزمة منفصلة.

نحن نكتب عميلًا لطلبات Skyscanner Flight Search API في المشروع

تفضلت Skyscanner بتقديم مقال حول كيفية استخدام واجهة برمجة التطبيقات (API) الخاصة بها (لن نقوم بإنشاء جلسة بطلب نشط). ماذا يعني "كتابة العميل"؟ نحتاج إلى إنشاء طلب إلى عنوان URL محدد بمعلمات معينة وإعداد DTO (كائن نقل البيانات) للبيانات المنقولة إلينا مرة أخرى. هناك أربع مجموعات من الطلبات في الموقع:
  1. البحث المباشر عن الرحلات الجوية - لن نعتبره غير ضروري في الوقت الحالي.
  2. الأماكن - دعونا نكتب.
  3. تصفح أسعار رحلات الطيران - سنستخدم طلبًا واحدًا يمكنك من خلاله الحصول على جميع المعلومات.
  4. التعريب - دعنا نضيفه حتى نعرف البيانات المدعومة.

إنشاء خدمة عميل لطلب الترجمة:

الخطة بسيطة مثل اللفت المطهو ​​على البخار: أنشئ طلبًا، وانظر إلى المعلمات، وانظر إلى الاستجابة. هناك نوعان من الاستعلامات: علامات القائمة والعملات. لنبدأ بالعملات. يوضح الشكل أن هذا طلب بدون حقول إضافية: وهو ضروري للحصول على معلومات حول العملات المدعومة: إنشاء نظام لمراقبة أسعار تذاكر الطيران: دليل خطوة بخطوة [الجزء الأول] - 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 ويقوم بإنشاء كافة أساليب الحروف والمحددات وتجاوزات toString() وequals() وhashCode(). ما يعمل على تحسين إمكانية قراءة التعليمات البرمجية وتسريع وقت كتابة كائنات POJO ؛
  • @JsonProperty("Code") هو تعليق توضيحي من مشروع جاكسون يوضح الحقل الذي سيتم تخصيصه لهذا المتغير. أي أنه سيتم تعيين حقل في 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();

}
وتنفيذ LocalizationClientImpl
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 عبارة عن تعليق توضيحي يشير إلى أنك بحاجة إلى إدخال كائن في هذه الفئة واستخدامه دون إنشائه، أي بدون عملية الكائن الجديد؛
  • @Component هو تعليق توضيحي ينص على أنه يجب إضافة هذا الكائن إلى سياق التطبيق بحيث يمكن إدخاله لاحقًا باستخدام التعليق التوضيحي @Autowired؛
  • ObjectMapper objectMapper هو كائن من مشروع Jackson الذي يترجم كل شيء إلى كائنات Java.
  • العملةDTO والبلدDto:
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. سعر الرحلةالعميل:
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