feat:Add check out API
This commit is contained in:
@@ -45,6 +45,9 @@ public class EsignAuditLogService {
|
||||
public static final String EVT_OHIP_PUT_PROFILE = "OHIP_PUT_PROFILE";
|
||||
public static final String EVT_OHIP_POST_AUTO_ROOM_ASSIGNMENT = "OHIP_POST_AUTO_ROOM_ASSIGNMENT";
|
||||
public static final String EVT_OHIP_POST_CHECK_IN = "OHIP_POST_CHECK_IN";
|
||||
public static final String EVT_OHIP_POST_CHECK_OUT_SEARCH = "OHIP_POST_CHECK_OUT_SEARCH";
|
||||
public static final String EVT_OHIP_POST_CHECK_OUT_PAYMENT = "OHIP_POST_CHECK_OUT_PAYMENT";
|
||||
public static final String EVT_OHIP_POST_CHECK_OUT = "OHIP_POST_CHECK_OUT";
|
||||
public static final String EVT_OHIP_PUT_STAY_EXTENSION = "OHIP_PUT_STAY_EXTENSION";
|
||||
|
||||
@Resource(name = "esignAuditLogRepository")
|
||||
|
||||
+14
@@ -2,6 +2,7 @@ package com.sjmresorts.opsapp.booking.reservation.controllers;
|
||||
|
||||
import com.sjmresorts.opsapp.esign.common.EsignApiPaths;
|
||||
import com.sjmresorts.opsapp.esign.reservation.dtos.BookingCheckInResult;
|
||||
import com.sjmresorts.opsapp.esign.reservation.dtos.BookingCheckOutResult;
|
||||
import com.sjmresorts.opsapp.esign.reservation.dtos.BookingStayExtensionRequest;
|
||||
import com.sjmresorts.opsapp.esign.reservation.dtos.BookingStayExtensionResult;
|
||||
import com.sjmresorts.opsapp.esign.reservation.services.ReservationThirdPartyClient;
|
||||
@@ -39,6 +40,19 @@ public class BookingCheckInController {
|
||||
return ResponseEntity.status(resolveHttpStatus(result.statusCode())).body(result);
|
||||
}
|
||||
|
||||
@PostMapping("/{reservationId}/check-out")
|
||||
@Operation(summary = "办理 Booking 自动退房")
|
||||
public ResponseEntity<BookingCheckOutResult> checkOut(
|
||||
@PathVariable String reservationId,
|
||||
@RequestParam(required = false) String hotelId) {
|
||||
if (StringUtils.isBlank(reservationId)) {
|
||||
return ResponseEntity.badRequest().body(BookingCheckOutResult.failure(
|
||||
400, reservationId, null, null, "reservationId required", null, null, null));
|
||||
}
|
||||
BookingCheckOutResult result = reservationThirdPartyClient.checkOut(hotelId, reservationId);
|
||||
return ResponseEntity.status(resolveHttpStatus(result.statusCode())).body(result);
|
||||
}
|
||||
|
||||
@PutMapping("/{reservationId}/stay")
|
||||
@Operation(summary = "续房并修改 Booking 离店日期")
|
||||
public ResponseEntity<BookingStayExtensionResult> extendStay(
|
||||
|
||||
+176
@@ -5,6 +5,7 @@ import com.sjmresorts.opsapp.common.utils.SimpleHttpClient;
|
||||
import com.sjmresorts.opsapp.esign.audit.services.EsignAuditLogService;
|
||||
import com.sjmresorts.opsapp.esign.audit.support.ThirdPartyAuditDetailBuilder;
|
||||
import com.sjmresorts.opsapp.esign.reservation.dtos.BookingCheckInResult;
|
||||
import com.sjmresorts.opsapp.esign.reservation.dtos.BookingCheckOutResult;
|
||||
import com.sjmresorts.opsapp.esign.reservation.dtos.BookingStayExtensionResult;
|
||||
import com.sjmresorts.opsapp.esign.reservation.dtos.ReservationThirdPartyResult;
|
||||
import com.sjmresorts.opsapp.esign.reservation.dtos.SignedRegistrationCardUploadResult;
|
||||
@@ -25,6 +26,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
@@ -80,6 +82,18 @@ public class ReservationThirdPartyClient {
|
||||
@Value("${ops-app.esign.reservation.third-party.booking-stay-extension.path-template:/rsv/v1/hotels/{hotelId}/reservations/{reservationId}}")
|
||||
private String bookingStayExtensionPathTemplate;
|
||||
|
||||
@Value("${ops-app.esign.reservation.third-party.booking-check-out.search.path-template:/rsv/v1/hotels/{hotelId}/reservations/searches}")
|
||||
private String checkOutSearchPathTemplate;
|
||||
|
||||
@Value("${ops-app.esign.reservation.third-party.booking-check-out.payment.path-template:/csh/v1/hotels/{hotelId}/reservations/{reservationId}/payments}")
|
||||
private String checkOutPaymentPathTemplate;
|
||||
|
||||
@Value("${ops-app.esign.reservation.third-party.booking-check-out.check-out.path-template:/csh/v1/hotels/{hotelId}/reservations/{reservationId}/checkOuts}")
|
||||
private String checkOutPathTemplate;
|
||||
|
||||
@Value("${ops-app.esign.reservation.third-party.booking-check-out.cashier-id:104}")
|
||||
private int checkOutCashierId;
|
||||
|
||||
@Value("${ops-app.esign.reservation.third-party.file-attachment.path:/med/config/v1/fileAttachments}")
|
||||
private String fileAttachmentPath;
|
||||
|
||||
@@ -311,6 +325,168 @@ public class ReservationThirdPartyClient {
|
||||
checkInResponse.getStatusCode(), reservationKey, roomId, assignmentSummary, checkInSummary);
|
||||
}
|
||||
|
||||
public BookingCheckOutResult checkOut(String hotelId, String reservationId) {
|
||||
String reservationKey = StringUtils.trimToNull(reservationId);
|
||||
String ohipHotelId = resolveOhipHotelId(hotelId);
|
||||
if (StringUtils.isAnyBlank(baseUrl, ohipHotelId, reservationKey)) {
|
||||
return BookingCheckOutResult.failure(400, reservationId, null, null,
|
||||
"hotelId and reservationId required", null, null, null);
|
||||
}
|
||||
if (!oauthTokenSingleton.isConfigured()) {
|
||||
return BookingCheckOutResult.failure(503, reservationKey, null, null,
|
||||
"OHIP OAuth is not configured", null, null, null);
|
||||
}
|
||||
|
||||
JSONObject searchBody = new JSONObject();
|
||||
JSONArray reservationIdList = new JSONArray();
|
||||
try {
|
||||
reservationIdList.add(Long.parseLong(reservationKey));
|
||||
} catch (NumberFormatException e) {
|
||||
return BookingCheckOutResult.failure(400, reservationKey, null, null,
|
||||
"reservationId must be numeric", null, null, null);
|
||||
}
|
||||
searchBody.put("reservationIdList", reservationIdList);
|
||||
HttpResponseResult searchResponse;
|
||||
try {
|
||||
searchResponse = executePost(
|
||||
checkOutSearchPathTemplate.replace("{hotelId}", urlEncodePathSegment(ohipHotelId)),
|
||||
searchBody,
|
||||
ohipHotelId,
|
||||
EsignAuditLogService.EVT_OHIP_POST_CHECK_OUT_SEARCH,
|
||||
"POST check out reservation search reservationId=" + reservationKey);
|
||||
} catch (IllegalStateException e) {
|
||||
return BookingCheckOutResult.failure(502, reservationKey, null, null, e.getMessage(), null, null, null);
|
||||
}
|
||||
String searchSummary = truncate(searchResponse.getResponseContent());
|
||||
if (searchResponse.isTransportError()) {
|
||||
return BookingCheckOutResult.failure(502, reservationKey, null, null,
|
||||
StringUtils.defaultIfBlank(searchResponse.getErrorMessage(), "OHIP reservation search transport error"),
|
||||
searchSummary, null, null);
|
||||
}
|
||||
if (!searchResponse.isSuccessful()) {
|
||||
return BookingCheckOutResult.failure(searchResponse.getStatusCode(), reservationKey, null, null,
|
||||
extractOhipErrorMessage(searchResponse.bodyOrEmpty(),
|
||||
"OHIP reservation search returned HTTP " + searchResponse.getStatusCode()),
|
||||
searchSummary, null, null);
|
||||
}
|
||||
|
||||
JSONObject balance = extractReservationBalance(searchResponse.bodyOrEmpty());
|
||||
BigDecimal balanceAmount = balance != null ? balance.getBigDecimal("amount") : null;
|
||||
String currencyCode = balance != null ? balance.getString("currencyCode") : null;
|
||||
if (balanceAmount == null) {
|
||||
return BookingCheckOutResult.failure(502, reservationKey, null, currencyCode,
|
||||
"OHIP reservation search response does not contain roomStay balance amount",
|
||||
searchSummary, null, null);
|
||||
}
|
||||
|
||||
String paymentSummary = null;
|
||||
if (balanceAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
JSONObject paymentBody = new JSONObject();
|
||||
JSONObject criteria = new JSONObject();
|
||||
JSONObject paymentMethod = new JSONObject();
|
||||
paymentMethod.put("paymentMethod", "CA");
|
||||
criteria.put("paymentMethod", paymentMethod);
|
||||
JSONObject postingAmount = new JSONObject();
|
||||
postingAmount.put("amount", balanceAmount);
|
||||
postingAmount.put("currencyCode", currencyCode);
|
||||
criteria.put("postingAmount", postingAmount);
|
||||
criteria.put("cashierId", checkOutCashierId);
|
||||
paymentBody.put("criteria", criteria);
|
||||
HttpResponseResult paymentResponse;
|
||||
try {
|
||||
paymentResponse = executePost(
|
||||
checkOutPaymentPathTemplate
|
||||
.replace("{hotelId}", urlEncodePathSegment(ohipHotelId))
|
||||
.replace("{reservationId}", urlEncodePathSegment(reservationKey)),
|
||||
paymentBody,
|
||||
ohipHotelId,
|
||||
EsignAuditLogService.EVT_OHIP_POST_CHECK_OUT_PAYMENT,
|
||||
"POST check out payment reservationId=" + reservationKey + " amount=" + balanceAmount);
|
||||
} catch (IllegalStateException e) {
|
||||
return BookingCheckOutResult.failure(502, reservationKey, balanceAmount, currencyCode,
|
||||
e.getMessage(), searchSummary, null, null);
|
||||
}
|
||||
paymentSummary = truncate(paymentResponse.getResponseContent());
|
||||
if (paymentResponse.isTransportError()) {
|
||||
return BookingCheckOutResult.failure(502, reservationKey, balanceAmount, currencyCode,
|
||||
StringUtils.defaultIfBlank(paymentResponse.getErrorMessage(), "OHIP payment transport error"),
|
||||
searchSummary, paymentSummary, null);
|
||||
}
|
||||
if (!paymentResponse.isSuccessful()) {
|
||||
return BookingCheckOutResult.failure(paymentResponse.getStatusCode(), reservationKey, balanceAmount, currencyCode,
|
||||
extractOhipErrorMessage(paymentResponse.bodyOrEmpty(),
|
||||
"OHIP payment returned HTTP " + paymentResponse.getStatusCode()),
|
||||
searchSummary, paymentSummary, null);
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject checkOutBody = new JSONObject();
|
||||
JSONObject reservation = new JSONObject();
|
||||
reservation.put("cashierId", checkOutCashierId);
|
||||
reservation.put("autoCheckout", true);
|
||||
checkOutBody.put("reservation", reservation);
|
||||
HttpResponseResult checkOutResponse;
|
||||
try {
|
||||
checkOutResponse = executePost(
|
||||
checkOutPathTemplate
|
||||
.replace("{hotelId}", urlEncodePathSegment(ohipHotelId))
|
||||
.replace("{reservationId}", urlEncodePathSegment(reservationKey)),
|
||||
checkOutBody,
|
||||
ohipHotelId,
|
||||
EsignAuditLogService.EVT_OHIP_POST_CHECK_OUT,
|
||||
"POST check out reservationId=" + reservationKey);
|
||||
} catch (IllegalStateException e) {
|
||||
return BookingCheckOutResult.failure(502, reservationKey, balanceAmount, currencyCode,
|
||||
e.getMessage(), searchSummary, paymentSummary, null);
|
||||
}
|
||||
String checkOutSummary = truncate(checkOutResponse.getResponseContent());
|
||||
if (checkOutResponse.isTransportError()) {
|
||||
return BookingCheckOutResult.failure(502, reservationKey, balanceAmount, currencyCode,
|
||||
StringUtils.defaultIfBlank(checkOutResponse.getErrorMessage(), "OHIP check out transport error"),
|
||||
searchSummary, paymentSummary, checkOutSummary);
|
||||
}
|
||||
if (!checkOutResponse.isSuccessful()) {
|
||||
return BookingCheckOutResult.failure(checkOutResponse.getStatusCode(), reservationKey, balanceAmount, currencyCode,
|
||||
extractOhipErrorMessage(checkOutResponse.bodyOrEmpty(),
|
||||
"OHIP check out returned HTTP " + checkOutResponse.getStatusCode()),
|
||||
searchSummary, paymentSummary, checkOutSummary);
|
||||
}
|
||||
return BookingCheckOutResult.success(checkOutResponse.getStatusCode(), reservationKey, balanceAmount,
|
||||
currencyCode, searchSummary, paymentSummary, checkOutSummary);
|
||||
}
|
||||
|
||||
private HttpResponseResult executePost(
|
||||
String path,
|
||||
JSONObject body,
|
||||
String hotelId,
|
||||
String eventType,
|
||||
String message) {
|
||||
String url = SimpleHttpClient.joinUrl(baseUrl, path);
|
||||
SimpleHttpClient.RequestBuilder request = SimpleHttpClient.post(url)
|
||||
.timeoutMs(timeoutMs)
|
||||
.jsonBody(body.toJSONString());
|
||||
applyBusinessAuth(request, hotelId);
|
||||
long start = System.currentTimeMillis();
|
||||
HttpResponseResult response = request.execute();
|
||||
auditThirdParty(eventType, request, response, start, hotelId, message);
|
||||
return response;
|
||||
}
|
||||
|
||||
private static JSONObject extractReservationBalance(String responseBody) {
|
||||
JSONObject root = JSONObject.parseObject(responseBody);
|
||||
JSONObject reservations = root.getJSONObject("reservations");
|
||||
if (reservations == null) {
|
||||
return null;
|
||||
}
|
||||
JSONArray reservationInfo = reservations.getJSONArray("reservationInfo");
|
||||
if (reservationInfo == null || reservationInfo.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
JSONObject reservation = reservationInfo.getJSONObject(0);
|
||||
JSONObject roomStay = reservation != null ? reservation.getJSONObject("roomStay") : null;
|
||||
return roomStay != null ? roomStay.getJSONObject("balance") : null;
|
||||
}
|
||||
|
||||
public BookingStayExtensionResult extendStay(String hotelId, String reservationId, LocalDate departureDate) {
|
||||
String reservationKey = StringUtils.trimToNull(reservationId);
|
||||
String ohipHotelId = resolveOhipHotelId(hotelId);
|
||||
|
||||
@@ -145,6 +145,14 @@ ops-app:
|
||||
path-template: /fof/v1/hotels/{hotelId}/reservations/autoRoomAssignments
|
||||
check-in:
|
||||
path-template: /fof/v1/hotels/{hotelId}/reservations/{reservationId}/checkIns
|
||||
booking-check-out:
|
||||
search:
|
||||
path-template: /rsv/v1/hotels/{hotelId}/reservations/searches
|
||||
payment:
|
||||
path-template: /csh/v1/hotels/{hotelId}/reservations/{reservationId}/payments
|
||||
check-out:
|
||||
path-template: /csh/v1/hotels/{hotelId}/reservations/{reservationId}/checkOuts
|
||||
cashier-id: 104
|
||||
file-attachment:
|
||||
path: /med/config/v1/fileAttachments
|
||||
user-name: USER@HOTEL
|
||||
|
||||
@@ -142,6 +142,14 @@ ops-app:
|
||||
path-template: /fof/v1/hotels/{hotelId}/reservations/autoRoomAssignments
|
||||
check-in:
|
||||
path-template: /fof/v1/hotels/{hotelId}/reservations/{reservationId}/checkIns
|
||||
booking-check-out:
|
||||
search:
|
||||
path-template: /rsv/v1/hotels/{hotelId}/reservations/searches
|
||||
payment:
|
||||
path-template: /csh/v1/hotels/{hotelId}/reservations/{reservationId}/payments
|
||||
check-out:
|
||||
path-template: /csh/v1/hotels/{hotelId}/reservations/{reservationId}/checkOuts
|
||||
cashier-id: 104
|
||||
file-attachment:
|
||||
path: /med/config/v1/fileAttachments
|
||||
user-name: USER@HOTEL
|
||||
|
||||
@@ -36,6 +36,14 @@ ops-app:
|
||||
batch-max: 2000
|
||||
reservation:
|
||||
third-party:
|
||||
booking-check-out:
|
||||
search:
|
||||
path-template: /rsv/v1/hotels/{hotelId}/reservations/searches
|
||||
payment:
|
||||
path-template: /csh/v1/hotels/{hotelId}/reservations/{reservationId}/payments
|
||||
check-out:
|
||||
path-template: /csh/v1/hotels/{hotelId}/reservations/{reservationId}/checkOuts
|
||||
cashier-id: 104
|
||||
profile-fields:
|
||||
enabled: true
|
||||
path-template: /crm/v1/profiles/{profileId}
|
||||
|
||||
Reference in New Issue
Block a user