JavaRush /จาวาบล็อก /Random-TH /การสร้างระบบติดตามราคาตั๋วเครื่องบิน: คำแนะนำทีละขั้นตอน ...
Roman Beekeeper
ระดับ

การสร้างระบบติดตามราคาตั๋วเครื่องบิน: คำแนะนำทีละขั้นตอน [ตอนที่ 1]

เผยแพร่ในกลุ่ม

เนื้อหา:

การสร้างระบบติดตามราคาตั๋วเครื่องบิน: คำแนะนำทีละขั้นตอน [ตอนที่ 1] - 1สวัสดีทุกคน ชุมชน JavaRush! วันนี้เราจะมาพูดถึงวิธีเขียนแอปพลิเคชัน Spring Boot เพื่อติดตามราคาตั๋วเครื่องบินทีละขั้นตอน บทความนี้มีไว้สำหรับผู้ที่มีแนวคิดเกี่ยวกับ:
  • REST และวิธีสร้างจุดสิ้นสุด REST
  • ฐานข้อมูลเชิงสัมพันธ์
  • งานของ Maven (โดยเฉพาะการพึ่งพาคืออะไร);
  • วัตถุ 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 ไปยังคลาสและย้อนกลับ เราจะใช้ Jackson Project ( นี่คือลิงก์ในแหล่งข้อมูลของเรา ) เราจะใช้spring-boot-starter-mail เพื่อส่งข้อความถึงผู้ใช้ เพื่อให้แอปพลิเคชันคำนวณราคาขั้นต่ำใหม่ตามความถี่ที่กำหนด เราจะใช้Spring Scheduler ในการสร้าง REST API เราจะใช้ spring- boot -starter-web เพื่อไม่ให้เขียนโค้ดที่ยืมมา (getters, setters, override equals และ hashcode, toString() สำหรับอ็อบเจ็กต์) เราจะใช้Project Lombok หากต้องการสัมผัสและเห็น REST API เราจะใช้ Swagger 2 และ Swagger UI (อินเทอร์เฟซผู้ใช้) ในทันทีสำหรับการติดตามแบบเรียลไทม์ หน้าตาตอนนี้จะเป็นดังนี้: การสร้างระบบติดตามราคาตั๋วเครื่องบิน: คำแนะนำทีละขั้นตอน [ตอนที่ 1] - 2โดยที่มีคำถามที่เหลือ 4 รายการที่เกี่ยวข้องกับการสร้าง แก้ไข การรับ และการลบการสมัครรับข้อมูล

สำรวจ Skyscanner API

ไปตามลิงค์ไปยังrakuten api ก่อนอื่น คุณต้องลงทะเบียน การสร้างระบบติดตามราคาตั๋วเครื่องบิน: คำแนะนำทีละขั้นตอน [ตอนที่ 1] - 3ทั้งหมดนี้จำเป็นเพื่อรับคีย์เฉพาะเพื่อใช้ไซต์และร้องขอไปยัง API สาธารณะที่โพสต์ไว้ หนึ่งใน API เหล่านี้คือSkyscanner Flight Search ที่เรา ต้องการ ตอนนี้เรามาดูกันว่ามันทำงานอย่างไร มาหาคำขอ 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จะออกให้ภายหลังการลงทะเบียน เพื่อติดตามการลดราคา เราจำเป็นต้อง มี จุดสิ้นสุดเรียกดูราคา หาเอาเอง :)

การสร้างกรอบงานแอปพลิเคชันโดยใช้ Spring Boot

หากต้องการสร้างโปรเจ็กต์ด้วย Spring Boot อย่างรวดเร็วและง่ายดาย คุณสามารถใช้Spring Initializrได้ เลือกตัวเลือกต่อไปนี้:
  1. โครงการมาเวน
  2. ชวา
  3. 2.1.10
  4. group - แล้วแต่คุณคิดว่าจำเป็น เช่น ru.javarush
  5. สิ่งประดิษฐ์ - เหมือนกันทุกประการ เช่น การตรวจสอบเที่ยวบิน
  6. ในการค้นหาการพึ่งพาเรามองหาสิ่งต่อไปนี้:
    • สปริงเว็บ
    • ผู้ส่งเมล Java
    • สปริงดาต้า เจพีเอ
    • ฐานข้อมูล H2
จากนั้นคลิกสร้าง เพียงเท่านี้ โครงการที่เสร็จแล้วจะถูกดาวน์โหลดเป็นไฟล์เก็บถาวร หากมีบางอย่างไม่ได้ผล คุณสามารถใช้ลิงก์ที่ฉันบันทึกโครงการที่ต้องการได้ แน่นอนว่า ควรทำด้วยตัวเองและทำความเข้าใจวิธีการทำงานจะดีกว่า แอปพลิเคชันจะประกอบด้วยสามชั้น:
  • ตัวควบคุม - เข้าสู่แอปพลิเคชัน REST API จะอธิบายไว้ที่นี่
  • บริการเป็นชั้นตรรกะทางธุรกิจ ตรรกะทั้งหมดของแอปพลิเคชันจะอธิบายไว้ที่นี่
  • พื้นที่เก็บข้อมูล - เลเยอร์สำหรับการทำงานกับฐานข้อมูล
นอกจากนี้ คลาสที่เกี่ยวข้องกับไคลเอนต์สำหรับ Skyscanner Flight Search API จะเป็นแพ็คเกจแยกต่างหาก

เรากำลังเขียนคำขอไปยังลูกค้าเพื่อส่งคำขอไปยัง Skyscanner Flight Search API ในโครงการ

Skyscanner ได้กรุณาจัดเตรียมบทความเกี่ยวกับวิธีใช้ API ของพวกเขา (เราจะไม่สร้างเซสชันที่มีคำขอที่ทำงานอยู่) "เขียนลูกค้า" หมายความว่าอย่างไร? เราจำเป็นต้องสร้างคำขอไปยัง URL เฉพาะด้วยพารามิเตอร์บางอย่างและเตรียม DTO (วัตถุการถ่ายโอนข้อมูล) สำหรับข้อมูลที่ถ่ายโอนกลับมาหาเรา มีคำขอสี่กลุ่มบนเว็บไซต์:
  1. ค้นหาเที่ยวบินสด - เราจะไม่พิจารณาว่าไม่จำเป็นในขณะนี้
  2. สถานที่ - มาเขียนกันเถอะ
  3. เรียกดูราคาเที่ยวบิน - เราจะใช้คำขอเดียวซึ่งคุณจะได้รับข้อมูลทั้งหมด
  4. การแปลเป็นภาษาท้องถิ่น - มาเพิ่มกันเพื่อให้เราทราบว่าข้อมูลใดบ้างที่รองรับ

สร้างบริการลูกค้าสำหรับคำขอ Localization:

แผนนี้ง่ายเหมือนหัวผักกาดนึ่ง: สร้างคำขอ ดูพารามิเตอร์ ดูการตอบสนอง มีสองคำถาม: เครื่องหมายรายการและสกุลเงิน เริ่มต้นด้วยสกุลเงิน รูปแสดงให้เห็นว่านี่เป็นคำขอที่ไม่มีฟิลด์เพิ่มเติม: จำเป็นเพื่อรับข้อมูลเกี่ยวกับสกุลเงินที่รองรับ: การสร้างระบบติดตามราคาตั๋วเครื่องบิน: คำแนะนำทีละขั้นตอน [ตอนที่ 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 เป็นคำอธิบายประกอบจากโปรเจ็กต์ลอมบอกและสร้างเมธอด getters, setters, overrides toString(), equals() และ hashCode() ทั้งหมด สิ่งที่ปรับปรุงความสามารถในการอ่านโค้ดและเพิ่มความเร็วในการเขียนอ็อบเจ็กต์POJO
  • @JsonProperty("Code") เป็นคำอธิบายประกอบจาก Jackson Project ที่บอกว่าฟิลด์ใดจะถูกกำหนดให้กับตัวแปรนี้ นั่นคือฟิลด์ใน 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 Project ที่แปลทั้งหมดนี้ลงในวัตถุ 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 เราจะดำเนินการตามคำขอเดียวเท่านั้น คือ browserQuotes ราคาเที่ยวบินลูกค้า:
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);
}
เที่ยวบินราคาClientImpl
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