update
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>21</java.version>
|
||||
<tomcat.version>11.0.25</tomcat.version>
|
||||
<tomcat.version>11.0.26</tomcat.version>
|
||||
<netty.version>4.2.18.Final</netty.version>
|
||||
<httpclient5.version>5.6.4</httpclient5.version>
|
||||
<httpclient5-win.version>5.2.3</httpclient5-win.version>
|
||||
@@ -29,6 +29,7 @@
|
||||
<fasterxml.jackson.version>2.22.3</fasterxml.jackson.version>
|
||||
<tools.jackson.version>3.2.3</tools.jackson.version>
|
||||
<log4j2.version>2.26.1</log4j2.version>
|
||||
<hibernate-validator.version>9.2.0.Beta1</hibernate-validator.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
</properties>
|
||||
@@ -92,7 +93,11 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-mail</artifactId>
|
||||
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<version>4.0.0-M1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -183,7 +188,7 @@
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
<version>4.6.1</version>
|
||||
<version>4.6.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.eatthepath</groupId>
|
||||
|
||||
@@ -24,8 +24,7 @@ public enum EumnDaylightStatus {
|
||||
DUEOUT("DUEOUT","DUE_OUT"),
|
||||
CANCELED("CANCELED","CANCELED"),
|
||||
CANCELLED("CANCELLED","CANCELED"),
|
||||
NO_SHOW("NO SHOW","CANCELED"),
|
||||
NOSHOW("NOSHOW","CANCELED");
|
||||
NO_SHOW("No-Show", "NO_SHOW");
|
||||
|
||||
private String key;
|
||||
private String value;
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.mgm.mcrm.apigateway.daylight.repositories.CfgCompRuleDao;
|
||||
import com.mgm.mcrm.apigateway.daylight.services.DaylightApiService;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl.AvailableRoomCountUpdatedHandler;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl.CompRuleLimitsExceededChangedHandler;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl.ReservationCustomFieldsNotificationHandler;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl.ReservationNotificationHandler;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl.RoomTypeDateAvailabilityChangedHandler;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.services.DaylightWebhookFactory;
|
||||
@@ -33,6 +34,8 @@ public class ConfigListener implements ApplicationListener<ApplicationStartedEve
|
||||
private BookingService bookingService;
|
||||
@Resource
|
||||
private DaylightApiService daylightApiService;
|
||||
@Resource
|
||||
private ReservationCustomFieldsNotificationHandler reservationCustomFieldsNotificationHandler;
|
||||
|
||||
@Autowired
|
||||
private SslBundles sslBundles;
|
||||
@@ -49,6 +52,7 @@ public class ConfigListener implements ApplicationListener<ApplicationStartedEve
|
||||
DaylightWebhookFactory.registerHandler("AvailableRoomCountUpdated", new AvailableRoomCountUpdatedHandler(roomCriteriaDaylightService));
|
||||
//--Register Reservation Notification handlers
|
||||
DaylightWebhookFactory.registerHandler("ReservationNotification", new ReservationNotificationHandler(bookingService, daylightApiService));
|
||||
DaylightWebhookFactory.registerHandler("ReservationCustomFieldsNotification", reservationCustomFieldsNotificationHandler);
|
||||
|
||||
try{
|
||||
HttpSslSupport.setSkipValidation(daylightApiSetting.isSkipTrustValidation());
|
||||
|
||||
+2
-2
@@ -6,13 +6,13 @@ import jakarta.annotation.Resource;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
//@RestController
|
||||
public class DaylightTestController {
|
||||
|
||||
@Resource
|
||||
private DaylightFetchConfigurationService daylightFetchConfigurationService;
|
||||
|
||||
@GetMapping("/test/fetch-daylight-config")
|
||||
// @GetMapping("/test/fetch-daylight-config")
|
||||
public BaseApiResponse<Void> testFetchDaylightConfiguration() {
|
||||
BaseApiResponse<Void> propertiesResponse = daylightFetchConfigurationService.fetchDaylightProperties();
|
||||
if (!propertiesResponse.isSuccess()) {
|
||||
|
||||
+45
-6
@@ -4,8 +4,8 @@ import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse;
|
||||
import com.mgm.mcrm.apigateway.daylight.common.services.DaylightFetchConfigurationService;
|
||||
import com.mgm.mcrm.apigateway.common.utils.LogUtil;
|
||||
import com.mgm.mcrm.apigateway.daylight.repositories.CfgDaylightConfigurationDao;
|
||||
import com.mgm.mcrm.apigateway.daylight.services.DaylightApiService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.core.annotation.Order;
|
||||
@@ -13,14 +13,17 @@ import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@ConditionalOnProperty(
|
||||
prefix = "daylight.scheduler",
|
||||
name = "open",
|
||||
havingValue = "true"
|
||||
)
|
||||
public class DaylightConfigurationScheduler {
|
||||
|
||||
@Resource
|
||||
private DaylightFetchConfigurationService daylightFetchConfigurationService;
|
||||
@Resource
|
||||
private CfgDaylightConfigurationDao cfgDaylightConfigurationDao;
|
||||
@Resource
|
||||
private DaylightApiService daylightApiService;
|
||||
|
||||
@Order(1)
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
@@ -93,6 +96,33 @@ public class DaylightConfigurationScheduler {
|
||||
}
|
||||
}
|
||||
|
||||
@Order(4)
|
||||
@EventListener(ApplicationReadyEvent.class)
|
||||
public void fetchResourceManagementRequestorsOnStartupIfMissing() {
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Checking saved Daylight resource requestors on startup");
|
||||
try {
|
||||
if (!cfgDaylightConfigurationDao.existsSavedDaylightProperties()) {
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "No saved Daylight properties found, skip resource requestors startup fetch");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cfgDaylightConfigurationDao.existsSavedDaylightResourceRequestors()) {
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Saved Daylight resource requestors found, skip startup fetch");
|
||||
return;
|
||||
}
|
||||
|
||||
LogUtil.warn(DaylightConfigurationScheduler.class, "No saved Daylight resource requestors found, start startup fetch");
|
||||
BaseApiResponse<Void> requestorResponse = daylightFetchConfigurationService.fetchResourceManagementRequestors();
|
||||
if (requestorResponse.isSuccess()) {
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Startup resource requestors completed: " + requestorResponse.getMessage());
|
||||
} else {
|
||||
LogUtil.warn(DaylightConfigurationScheduler.class, "Startup resource requestors failed: " + requestorResponse.getMessage());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(DaylightConfigurationScheduler.class, "Startup resource requestors error: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按配置的时间和时区执行,拉取 Daylight 配置数据,默认北京时间每天凌晨2点
|
||||
*/
|
||||
@@ -102,7 +132,7 @@ public class DaylightConfigurationScheduler {
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Start scheduled task: fetchDaylightAllData");
|
||||
try {
|
||||
// 1. 拉取 Properties
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Step 1/3: Fetching properties...");
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Step 1/4: Fetching properties...");
|
||||
BaseApiResponse<Void> propResponse = daylightFetchConfigurationService.fetchDaylightProperties();
|
||||
if (propResponse.isSuccess()) {
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Properties completed: " + propResponse.getMessage());
|
||||
@@ -111,7 +141,7 @@ public class DaylightConfigurationScheduler {
|
||||
}
|
||||
|
||||
// 2. 拉取 Configuration
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Step 2/3: Fetching configurations...");
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Step 2/4: Fetching configurations...");
|
||||
BaseApiResponse<Void> configResponse = daylightFetchConfigurationService.fetchDaylightConfiguration();
|
||||
if (configResponse.isSuccess()) {
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Configurations completed: " + configResponse.getMessage());
|
||||
@@ -120,7 +150,7 @@ public class DaylightConfigurationScheduler {
|
||||
}
|
||||
|
||||
// 3. 拉取 Block Codes
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Step 3/3: Fetching block codes...");
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Step 3/4: Fetching block codes...");
|
||||
BaseApiResponse<Void> blockResponse = daylightFetchConfigurationService.fetchDaylightBlockCodes();
|
||||
if (blockResponse.isSuccess()) {
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Block codes completed: " + blockResponse.getMessage());
|
||||
@@ -128,6 +158,15 @@ public class DaylightConfigurationScheduler {
|
||||
LogUtil.warn(DaylightConfigurationScheduler.class, "Block codes failed: " + blockResponse.getMessage());
|
||||
}
|
||||
|
||||
// 4. 拉取 Resource Requestors
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Step 4/4: Fetching resource requestors...");
|
||||
BaseApiResponse<Void> requestorResponse = daylightFetchConfigurationService.fetchResourceManagementRequestors();
|
||||
if (requestorResponse.isSuccess()) {
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Resource requestors completed: " + requestorResponse.getMessage());
|
||||
} else {
|
||||
LogUtil.warn(DaylightConfigurationScheduler.class, "Resource requestors failed: " + requestorResponse.getMessage());
|
||||
}
|
||||
|
||||
LogUtil.info(DaylightConfigurationScheduler.class, "Scheduled task finished: fetchDaylightAllData");
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(DaylightConfigurationScheduler.class, "Scheduled task error: " + e.getMessage());
|
||||
|
||||
+187
-2
@@ -1,6 +1,7 @@
|
||||
package com.mgm.mcrm.apigateway.daylight.common.services;
|
||||
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONArray;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.mgm.mcrm.apigateway.basesetting.property.models.CfgProperties;
|
||||
import com.mgm.mcrm.apigateway.basesetting.property.repositories.PropertyDao;
|
||||
@@ -30,9 +31,12 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class DaylightFetchConfigurationService {
|
||||
@@ -41,6 +45,8 @@ public class DaylightFetchConfigurationService {
|
||||
private static final DateTimeFormatter UTC_UPDATED_AT_FORMATTER = DateTimeFormatter
|
||||
.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS")
|
||||
.withZone(ZoneOffset.UTC);
|
||||
private static final String RESOURCE_REQUESTOR_CONFIG_NAME = "ResourceRequestor";
|
||||
private static final int RESOURCE_REQUESTOR_PAGE_SIZE = 200;
|
||||
|
||||
private final HttpProxyErrorHandler httpErrorHandler = new HttpProxyErrorHandler();
|
||||
@Resource
|
||||
@@ -93,7 +99,7 @@ public class DaylightFetchConfigurationService {
|
||||
boolean hasSavedConfig = cfgDaylightConfigurationDao.existsSavedConfigurationByDlPropertyId(dlPropertyId);
|
||||
if (lastSync.isPresent() && hasSavedConfig) {
|
||||
updatedAtParam = "&updatedAt="
|
||||
+ UTC_UPDATED_AT_FORMATTER.format(lastSync.get().getLastSyncAt().toInstant());
|
||||
+ UTC_UPDATED_AT_FORMATTER.format(lastSync.get().getLastSyncAt().toInstant().minusSeconds(1));
|
||||
log.info("Property {} incremental sync, updatedAt: {}", dlPropertyId, updatedAtParam);
|
||||
} else {
|
||||
if (lastSync.isPresent() && !hasSavedConfig) {
|
||||
@@ -506,7 +512,7 @@ public class DaylightFetchConfigurationService {
|
||||
.findByDlPropertyIdAndConfigName(dlPropertyId, "BlockCode");
|
||||
if (lastSync.isPresent() && existingConfig != null) {
|
||||
updatedAtParam = "&updatedAt="
|
||||
+ UTC_UPDATED_AT_FORMATTER.format(lastSync.get().getLastSyncAt().toInstant());
|
||||
+ UTC_UPDATED_AT_FORMATTER.format(lastSync.get().getLastSyncAt().toInstant().minusSeconds(1));
|
||||
log.info("Property {} BlockCode incremental sync, updatedAt: {}", dlPropertyId, updatedAtParam);
|
||||
} else {
|
||||
if (lastSync.isPresent() && existingConfig == null) {
|
||||
@@ -635,4 +641,183 @@ public class DaylightFetchConfigurationService {
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 property 分页拉取资源请求人;首次全量同步,之后按成功同步时间增量合并。
|
||||
*/
|
||||
public BaseApiResponse<Void> fetchResourceManagementRequestors() {
|
||||
BaseApiResponse<Void> response = new BaseApiResponse<>();
|
||||
Date syncStartTime = new Date();
|
||||
|
||||
try {
|
||||
TokenResponse daylightApiToken = daylightTokenService.getApiAccessToken();
|
||||
if (daylightApiToken == null || StringUtils.isBlank(daylightApiToken.getAccessToken())) {
|
||||
response.setSuccess(false);
|
||||
response.setMessage("Invoke daylight token failed");
|
||||
return response;
|
||||
}
|
||||
|
||||
List<CfgProperties> allProperties = propertyDao.findByCategory("DAYLIGHT");
|
||||
if (allProperties == null || allProperties.isEmpty()) {
|
||||
response.setSuccess(false);
|
||||
response.setMessage("No properties found with category 'DAYLIGHT'");
|
||||
return response;
|
||||
}
|
||||
|
||||
int savedCount = 0;
|
||||
List<String> failedProperties = new ArrayList<>();
|
||||
for (CfgProperties property : allProperties) {
|
||||
String dlPropertyId = property.getDlPropertyId();
|
||||
if (StringUtils.isBlank(dlPropertyId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean fetchSuccess = false;
|
||||
int recordCount = 0;
|
||||
try {
|
||||
CfgDaylightConfiguration existingConfig = cfgDaylightConfigurationDao
|
||||
.findByDlPropertyIdAndConfigName(dlPropertyId, RESOURCE_REQUESTOR_CONFIG_NAME);
|
||||
Optional<SysLogDaylightConfigurationSync> lastSync = syncLogDao
|
||||
.findTopByConfigNameAndDlPropertyIdAndSyncStatusOrderByLastSyncAtDesc(
|
||||
RESOURCE_REQUESTOR_CONFIG_NAME, dlPropertyId, "SUCCESS");
|
||||
|
||||
// 配置不存在时强制全量,两个日期条件均不传。
|
||||
boolean isIncremental = existingConfig != null && lastSync.isPresent()
|
||||
&& lastSync.get().getLastSyncAt() != null;
|
||||
String dateParams = "";
|
||||
if (isIncremental) {
|
||||
String syncTime = DateTimeFormatter.ISO_INSTANT.format(lastSync.get().getLastSyncAt().toInstant().minusSeconds(1));
|
||||
// 接口通过同一个 filter 接收两个日期条件,独立的日期 query 参数会被忽略。
|
||||
dateParams = "&filter=auditData.lastUpdateTime=ge=" + syncTime
|
||||
+ ",auditData.creationTime=ge=" + syncTime;
|
||||
log.info("Property {} ResourceRequestor incremental sync, UTC time: {}", dlPropertyId, syncTime);
|
||||
} else {
|
||||
log.info("Property {} ResourceRequestor full sync, no audit date parameters", dlPropertyId);
|
||||
}
|
||||
|
||||
List<JSONObject> allResults = fetchResourceRequestorPages(
|
||||
dlPropertyId, daylightApiToken.getAccessToken(), dateParams, response);
|
||||
List<JSONObject> oldItems = existingConfig != null
|
||||
&& StringUtils.isNotBlank(existingConfig.getConfigValue())
|
||||
? JSON.parseArray(existingConfig.getConfigValue(), JSONObject.class)
|
||||
: new ArrayList<>();
|
||||
|
||||
// 所有页面成功后才保存,增量没有变化时保留原配置。
|
||||
if (!isIncremental || !allResults.isEmpty()) {
|
||||
List<JSONObject> savedItems = allResults;
|
||||
if (isIncremental) {
|
||||
Map<String, JSONObject> mergedMap = new LinkedHashMap<>();
|
||||
for (JSONObject item : oldItems) {
|
||||
mergedMap.put(getConfigItemKey(item), item);
|
||||
}
|
||||
for (JSONObject item : allResults) {
|
||||
mergedMap.put(getConfigItemKey(item), item);
|
||||
}
|
||||
savedItems = new ArrayList<>(mergedMap.values());
|
||||
}
|
||||
|
||||
CfgDaylightConfiguration config = existingConfig != null ? existingConfig : new CfgDaylightConfiguration();
|
||||
config.setConfigName(RESOURCE_REQUESTOR_CONFIG_NAME);
|
||||
config.setDlPropertyId(dlPropertyId);
|
||||
if (config.getCreatedAt() == null) {
|
||||
config.setCreatedAt(new Date());
|
||||
}
|
||||
config.setConfigValue(JSONObject.toJSONString(savedItems));
|
||||
config.setUpdatedAt(new Date());
|
||||
cfgDaylightConfigurationDao.save(config);
|
||||
logConfigurationChanges("ResourceRequestor " + (isIncremental ? "incremental sync" : "full sync"),
|
||||
dlPropertyId, RESOURCE_REQUESTOR_CONFIG_NAME, oldItems, savedItems);
|
||||
savedCount++;
|
||||
}
|
||||
recordCount = allResults.size();
|
||||
fetchSuccess = true;
|
||||
} catch (Exception e) {
|
||||
failedProperties.add(dlPropertyId);
|
||||
log.warn("Property {} ResourceRequestor sync failed", dlPropertyId, e);
|
||||
}
|
||||
|
||||
SysLogDaylightConfigurationSync syncLog = new SysLogDaylightConfigurationSync();
|
||||
syncLog.setConfigName(RESOURCE_REQUESTOR_CONFIG_NAME);
|
||||
syncLog.setDlPropertyId(dlPropertyId);
|
||||
syncLog.setLastSyncAt(syncStartTime);
|
||||
syncLog.setSyncStatus(fetchSuccess ? "SUCCESS" : "FAILED");
|
||||
syncLog.setRecordCount(recordCount);
|
||||
syncLog.setCreatedAt(new Date());
|
||||
syncLogDao.save(syncLog);
|
||||
}
|
||||
|
||||
response.setSuccess(failedProperties.isEmpty());
|
||||
response.setMessage("Fetched and saved " + savedCount + " resource requestor configuration entries."
|
||||
+ (failedProperties.isEmpty() ? "" : " Failed properties: " + StringUtils.join(failedProperties, ",")));
|
||||
} catch (Exception e) {
|
||||
log.warn("ResourceRequestor sync failed", e);
|
||||
response.setSuccess(false);
|
||||
response.setMessage(e.getMessage());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
private List<JSONObject> fetchResourceRequestorPages(String dlPropertyId, String accessToken,
|
||||
String dateParams, BaseApiResponse<Void> response) throws Exception {
|
||||
String apiUrl = daylightApiSetting.getServerUrl() + "/api-gateway/resource-management/v1/resource";
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("authorization", "Bearer " + accessToken);
|
||||
headers.set("Ac-Property-Id", dlPropertyId);
|
||||
List<JSONObject> allResults = new ArrayList<>();
|
||||
int pageNum = 1;
|
||||
Set<String> fetchedIds = new HashSet<>();
|
||||
long totalCount;
|
||||
do {
|
||||
// 此接口的分页请求参数为 pageNumber;pageNum 会被忽略并重复返回第一页。
|
||||
String queryString = "pageSize=" + RESOURCE_REQUESTOR_PAGE_SIZE + "&pageNumber=" + pageNum + dateParams;
|
||||
log.info("Fetching ResourceRequestor page {} for Ac-Property-Id: {}, query: {}", pageNum, dlPropertyId, queryString);
|
||||
ResponseEntity<byte[]> requestResponse = webProxyService.invokeRequest(
|
||||
WebProxyService.DAYLIGHT_API, HttpMethod.GET, apiUrl, queryString, headers, new byte[0],
|
||||
Duration.ofSeconds(daylightApiSetting.getApiTimeout()), httpErrorHandler).block();
|
||||
if (requestResponse == null) {
|
||||
throw new IllegalStateException("Http request server can not visit");
|
||||
}
|
||||
response.setStatusCode(requestResponse.getStatusCode().value());
|
||||
if (!requestResponse.getStatusCode().is2xxSuccessful()) {
|
||||
byte[] errorBody = requestResponse.getBody();
|
||||
throw new IllegalStateException(errorBody != null
|
||||
? new String(errorBody, StandardCharsets.UTF_8) : "ResourceRequestor request failed");
|
||||
}
|
||||
byte[] responseBody = requestResponse.getBody();
|
||||
if (responseBody == null || responseBody.length == 0) {
|
||||
throw new IllegalStateException("ResourceRequestor response body is empty");
|
||||
}
|
||||
JSONObject responseJson = JSONObject.parseObject(new String(responseBody, StandardCharsets.UTF_8));
|
||||
JSONArray results = responseJson != null ? responseJson.getJSONArray("results") : null;
|
||||
JSONObject paging = responseJson != null ? responseJson.getJSONObject("paging") : null;
|
||||
Long pageTotalCount = paging != null ? paging.getLong("totalCount") : null;
|
||||
if (results == null || pageTotalCount == null || pageTotalCount < 0) {
|
||||
throw new IllegalStateException("ResourceRequestor response is missing valid results or paging.totalCount");
|
||||
}
|
||||
Integer responsePageNumber = paging.getInteger("pageNumber");
|
||||
if (responsePageNumber != null && responsePageNumber != pageNum) {
|
||||
throw new IllegalStateException("ResourceRequestor requested page " + pageNum
|
||||
+ " but received page " + responsePageNumber + "; pagination parameters were not applied");
|
||||
}
|
||||
totalCount = pageTotalCount;
|
||||
log.info("ResourceRequestor response for Ac-Property-Id: {}, requestedPage={}, responsePage={}, pageCount={}, totalCount={}",
|
||||
dlPropertyId, pageNum, responsePageNumber, results.size(), totalCount);
|
||||
if (results.isEmpty() && allResults.size() < totalCount) {
|
||||
throw new IllegalStateException("ResourceRequestor returned an empty page before all records were fetched");
|
||||
}
|
||||
List<JSONObject> pageResults = results.toJavaList(JSONObject.class);
|
||||
for (JSONObject item : pageResults) {
|
||||
String id = item.getString("id");
|
||||
if (StringUtils.isBlank(id) || !fetchedIds.add(id)) {
|
||||
throw new IllegalStateException("ResourceRequestor returned a missing or repeated resource id on page " + pageNum);
|
||||
}
|
||||
}
|
||||
allResults.addAll(pageResults);
|
||||
} while ((long) pageNum++ * RESOURCE_REQUESTOR_PAGE_SIZE < totalCount);
|
||||
|
||||
if (allResults.size() != totalCount) {
|
||||
throw new IllegalStateException("ResourceRequestor returned a record count different from paging.totalCount");
|
||||
}
|
||||
return allResults;
|
||||
}
|
||||
}
|
||||
|
||||
+9
-2
@@ -17,7 +17,7 @@ public interface CfgDaylightConfigurationDao extends JpaRepository<CfgDaylightCo
|
||||
|
||||
@Query("select case when count(c) > 0 then true else false end "
|
||||
+ "from CfgDaylightConfiguration c "
|
||||
+ "where c.configName not in ('Property', 'BlockCode') "
|
||||
+ "where c.configName not in ('Property', 'BlockCode', 'ResourceRequestor') "
|
||||
+ "and c.configValue is not null "
|
||||
+ "and c.configValue <> ''")
|
||||
boolean existsSavedDaylightConfiguration();
|
||||
@@ -25,7 +25,7 @@ public interface CfgDaylightConfigurationDao extends JpaRepository<CfgDaylightCo
|
||||
@Query("select case when count(c) > 0 then true else false end "
|
||||
+ "from CfgDaylightConfiguration c "
|
||||
+ "where c.dlPropertyId = ?1 "
|
||||
+ "and c.configName not in ('Property', 'BlockCode') "
|
||||
+ "and c.configName not in ('Property', 'BlockCode', 'ResourceRequestor') "
|
||||
+ "and c.configValue is not null "
|
||||
+ "and c.configValue <> ''")
|
||||
boolean existsSavedConfigurationByDlPropertyId(String dlPropertyId);
|
||||
@@ -36,4 +36,11 @@ public interface CfgDaylightConfigurationDao extends JpaRepository<CfgDaylightCo
|
||||
+ "and c.configValue is not null "
|
||||
+ "and c.configValue <> ''")
|
||||
boolean existsSavedDaylightBlockCodes();
|
||||
|
||||
@Query("select case when count(c) > 0 then true else false end "
|
||||
+ "from CfgDaylightConfiguration c "
|
||||
+ "where c.configName = 'ResourceRequestor' "
|
||||
+ "and c.configValue is not null "
|
||||
+ "and c.configValue <> ''")
|
||||
boolean existsSavedDaylightResourceRequestors();
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ public class DaylightApiService {
|
||||
BaseApiResponse<JSONObject> result = new BaseApiResponse<>();
|
||||
|
||||
try {
|
||||
String url = "/api-gateway/booking/v1/reservations/" + dlReservationId + "?extend=PurchaseElements,CustomFields";
|
||||
String url = "/api-gateway/booking/v1/reservations/" + dlReservationId + "?extend=CustomFields";
|
||||
// 获取对应的值
|
||||
Header[] headers = HttpHeaderBuilder.custom()
|
||||
.contentType("application/json")
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ReservationCustomFieldsNotificationEvent {
|
||||
private String reservationId;
|
||||
private String trigger;
|
||||
private CustomFieldsState previous;
|
||||
private CustomFieldsState current;
|
||||
|
||||
@Data
|
||||
public static class CustomFieldsState {
|
||||
private List<CustomField> customFields;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class CustomField {
|
||||
private String location;
|
||||
private String value;
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation;
|
||||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ReservationHeaderCustomFields {
|
||||
@JSONField(name = "Booker")
|
||||
private String booker;
|
||||
|
||||
@JSONField(name = "Approver")
|
||||
private String approver;
|
||||
|
||||
@JSONField(name = "App_Rea")
|
||||
private String approvalReason;
|
||||
|
||||
@JSONField(name = "WAIVEDEPOSIT", alternateNames = {"WAIVEDPOSIT"})
|
||||
private String waiveDeposit;
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation.ReservationCustomFieldsNotificationEvent;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation.ReservationCustomFieldsNotificationEvent.CustomField;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation.ReservationHeaderCustomFields;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.DaylightWebhookHandler;
|
||||
import com.mgm.mcrm.apigateway.reservation.models.Bookings;
|
||||
import com.mgm.mcrm.apigateway.reservation.repositories.BookingDao;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ReservationCustomFieldsNotificationHandler implements DaylightWebhookHandler {
|
||||
|
||||
private final BookingDao bookingDao;
|
||||
|
||||
@Override
|
||||
public BaseApiResponse<Void> handle(String payload, Map<String, String> headers) throws InterruptedException {
|
||||
BaseApiResponse<Void> result = new BaseApiResponse<>();
|
||||
ReservationCustomFieldsNotificationEvent eventData;
|
||||
try {
|
||||
eventData = JSONObject.parseObject(payload, ReservationCustomFieldsNotificationEvent.class);
|
||||
} catch (Exception err) {
|
||||
result.setMessage("Convert Payload failed:[" + err.getMessage() + "]");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (eventData == null || StringUtils.isBlank(eventData.getReservationId())) {
|
||||
result.setMessage("Empty reservationId for this event");
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
TimeUnit.SECONDS.sleep(8);
|
||||
} catch (InterruptedException err) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw err;
|
||||
}
|
||||
|
||||
String reservationId = eventData.getReservationId();
|
||||
List<Bookings> bookings = bookingDao.findByDlReservationId(reservationId);
|
||||
Bookings booking = null;
|
||||
if (!CollectionUtils.isEmpty(bookings)) {
|
||||
for (Bookings candidate : bookings) {
|
||||
if ("N".equals(candidate.getApproveStatus())) {
|
||||
booking = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (booking == null) {
|
||||
log.warn("No booking with approveStatus=N for reservationId={}", reservationId);
|
||||
result.setMessage("No booking with approveStatus=N for reservationId[" + reservationId + "]");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (eventData.getCurrent() == null || CollectionUtils.isEmpty(eventData.getCurrent().getCustomFields())) {
|
||||
result.setMessage("Empty current.customFields for this event");
|
||||
return result;
|
||||
}
|
||||
|
||||
CustomField reservationHeader = eventData.getCurrent().getCustomFields().stream()
|
||||
.filter(field -> field != null && "ReservationHeader".equals(field.getLocation()))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (reservationHeader == null || StringUtils.isBlank(reservationHeader.getValue())) {
|
||||
result.setMessage("Empty ReservationHeader value for this event");
|
||||
return result;
|
||||
}
|
||||
|
||||
ReservationHeaderCustomFields customFields;
|
||||
Long approvedId;
|
||||
try {
|
||||
customFields = JSONObject.parseObject(reservationHeader.getValue(), ReservationHeaderCustomFields.class);
|
||||
if (customFields == null) {
|
||||
result.setMessage("Empty ReservationHeader value for this event");
|
||||
return result;
|
||||
}
|
||||
approvedId = StringUtils.isBlank(customFields.getApprover())
|
||||
? null : Long.valueOf(customFields.getApprover().trim());
|
||||
} catch (Exception err) {
|
||||
result.setMessage("Convert ReservationHeader value failed:[" + err.getMessage() + "]");
|
||||
return result;
|
||||
}
|
||||
|
||||
booking.setCreatedBy(customFields.getBooker());
|
||||
booking.setBooker(customFields.getBooker());
|
||||
booking.setApprovalReason(customFields.getApprovalReason());
|
||||
booking.setApprovedId(approvedId);
|
||||
bookingDao.save(booking);
|
||||
|
||||
log.info("Updated reservation custom fields for bookingId={}, reservationId={}", booking.getId(), reservationId);
|
||||
result.setSuccess(true);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+62
-44
@@ -10,11 +10,13 @@ import com.mgm.mcrm.apigateway.daylight.services.DaylightApiService;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation.ReservationNotificationEvent;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation.StatusCodeEntity;
|
||||
import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.DaylightWebhookHandler;
|
||||
//import com.mgm.mcrm.apigateway.logging.services.SysBookingCacheService;
|
||||
import com.mgm.mcrm.apigateway.reservation.models.Bookings;
|
||||
import com.mgm.mcrm.apigateway.reservation.services.BookingService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Strings;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -32,6 +34,8 @@ public class ReservationNotificationHandler implements DaylightWebhookHandler {
|
||||
|
||||
private final DaylightApiService daylightApiService;
|
||||
|
||||
// private final SysBookingCacheService sysBookingCacheService;
|
||||
|
||||
@Value("${daylight.system-id}")
|
||||
private String systemId;
|
||||
|
||||
@@ -103,7 +107,17 @@ public class ReservationNotificationHandler implements DaylightWebhookHandler {
|
||||
|
||||
StatusCodeEntity statusCode = eventData.getStatusCode();
|
||||
if(statusCode!=null) {
|
||||
result.setLastOperaStatus(EumnDaylightStatus.getStatusByDaylightStatus(statusCode.getDescription().toUpperCase()));
|
||||
String lastOperaStatus = EumnDaylightStatus.getStatusByDaylightStatus(statusCode.getDescription().toUpperCase());
|
||||
|
||||
result.setLastOperaStatus(lastOperaStatus);
|
||||
|
||||
if (Strings.CI.equalsAny(lastOperaStatus, "RESERVED", "CHECKED_IN", "DUE_IN")) {
|
||||
result.setApproveStatus("N");
|
||||
} else if ("CANCELED".equalsIgnoreCase(lastOperaStatus)) {
|
||||
result.setApproveStatus("C");
|
||||
} else {
|
||||
result.setApproveStatus("N");
|
||||
}
|
||||
}
|
||||
result.setDlPropertyId(eventData.getPropertyId());
|
||||
result.setDlRoomTypeId(eventData.getRoomTypeId());
|
||||
@@ -113,49 +127,53 @@ public class ReservationNotificationHandler implements DaylightWebhookHandler {
|
||||
String etaTime = dateTime.format(DateTimeFormatter.ofPattern("HH:mm"));
|
||||
result.setEta(etaTime);
|
||||
}
|
||||
BaseApiResponse<JSONObject> response =
|
||||
daylightApiService.getDaylightReservationByDlId(eventData.getReservationId(), eventData.getPropertyId());
|
||||
|
||||
if (response.isSuccess() && Objects.nonNull(response.getData())) {
|
||||
JSONObject embedded = response.getData().getJSONObject("_embedded");
|
||||
if (embedded != null) {
|
||||
JSONArray customFields = embedded.getJSONArray("customFields");
|
||||
|
||||
if (customFields != null) {
|
||||
for (int j = 0; j < customFields.size(); j++) {
|
||||
JSONObject jsonObject = customFields.getJSONObject(j);
|
||||
String value = jsonObject.getString("value");
|
||||
JSONObject valueJson = JSONObject.parseObject(value);
|
||||
String booker = valueJson.getString("Booker");
|
||||
|
||||
if (StringUtils.isNotBlank(booker)) {
|
||||
result.setCreatedBy(booker);
|
||||
result.setBooker(booker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(result.getDlExternalNo())) {
|
||||
JSONArray jsonArray = response.getData().getJSONArray("externalIds");
|
||||
if (!CollectionUtils.isEmpty(jsonArray)) {
|
||||
|
||||
for (int i = 0; i < jsonArray.size(); i++) {
|
||||
JSONObject jsonObject = jsonArray.getJSONObject(i);
|
||||
|
||||
if (jsonObject.getString("systemId").equals(systemId)) {
|
||||
result.setDlExternalNo(jsonObject.getString("number"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(result.getDlExternalNo())) {
|
||||
String dlExternalNo = jsonArray.getJSONObject(0).getString("number");
|
||||
result.setDlExternalNo(dlExternalNo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// BaseApiResponse<JSONObject> response =
|
||||
// daylightApiService.getDaylightReservationByDlId(eventData.getReservationId(), eventData.getPropertyId());
|
||||
//
|
||||
// if (response.isSuccess()) {
|
||||
// if (Objects.nonNull(response.getData())) {
|
||||
// JSONObject embedded = response.getData().getJSONObject("_embedded");
|
||||
// if (embedded != null) {
|
||||
// JSONArray customFields = embedded.getJSONArray("customFields");
|
||||
//
|
||||
// if (customFields != null) {
|
||||
// for (int j = 0; j < customFields.size(); j++) {
|
||||
// JSONObject jsonObject = customFields.getJSONObject(j);
|
||||
// String value = jsonObject.getString("value");
|
||||
// JSONObject valueJson = JSONObject.parseObject(value);
|
||||
// String booker = valueJson.getString("Booker");
|
||||
//
|
||||
// if (StringUtils.isNotBlank(booker)) {
|
||||
// result.setCreatedBy(booker);
|
||||
// result.setBooker(booker);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (StringUtils.isBlank(result.getDlExternalNo())) {
|
||||
// JSONArray jsonArray = response.getData().getJSONArray("externalIds");
|
||||
// if (!CollectionUtils.isEmpty(jsonArray)) {
|
||||
//
|
||||
// for (int i = 0; i < jsonArray.size(); i++) {
|
||||
// JSONObject jsonObject = jsonArray.getJSONObject(i);
|
||||
//
|
||||
// if (jsonObject.getString("systemId").equals(systemId)) {
|
||||
// result.setDlExternalNo(jsonObject.getString("number"));
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if (StringUtils.isBlank(result.getDlExternalNo())) {
|
||||
// String dlExternalNo = jsonArray.getJSONObject(0).getString("number");
|
||||
// result.setDlExternalNo(dlExternalNo);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// log.warn("reservationId={} not found _embedded", eventData.getReservationId());
|
||||
// }
|
||||
// }
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mgm.mcrm.apigateway.logging.models;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Entity
|
||||
@Table(name="sys_booking_profile_mapping")
|
||||
public class SysBookingProfileMapping {
|
||||
@Id
|
||||
@Column(name = "id")
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
@Column(name="category")
|
||||
private String category;
|
||||
@Column(name="player_id")
|
||||
private String playerId;
|
||||
@Column(name="profile_id")
|
||||
private String profileId;
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package com.mgm.mcrm.apigateway.logging.repositories;
|
||||
|
||||
import com.mgm.mcrm.apigateway.logging.models.SysBookingProfileMapping;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface SysBookingProfileMappingDao extends JpaRepository<SysBookingProfileMapping, Long> {
|
||||
List<SysBookingProfileMapping> findAllByCategoryAndPlayerId(String category, String playerId);
|
||||
|
||||
List<String> findByProfileId(String profileId);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.mgm.mcrm.apigateway.logging.services;
|
||||
|
||||
import com.mgm.mcrm.apigateway.logging.repositories.SysBookingProfileMappingDao;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class SysBookingCacheService {
|
||||
|
||||
@Value("${mcrm.daylight.booking-search-cache-seconds}")
|
||||
private int daylightCacheSeconds;
|
||||
|
||||
@Resource
|
||||
private SysBookingProfileMappingDao sysBookingProfileMappingDao;
|
||||
|
||||
public String getPlayerIdFromProfileId(String profileId) {
|
||||
|
||||
if (StringUtils.isEmpty(profileId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> result = sysBookingProfileMappingDao.findByProfileId(profileId);
|
||||
|
||||
if (CollectionUtils.isEmpty(result)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return result.getFirst();
|
||||
}
|
||||
}
|
||||
@@ -105,6 +105,8 @@ public class Bookings implements Serializable {
|
||||
private String mscRemark;
|
||||
@Column(name="approval_remark")
|
||||
private String approvalRemark;
|
||||
@Column(name="approval_reason")
|
||||
private String approvalReason;
|
||||
@Column(name="room")
|
||||
private String room;
|
||||
@Column(name="sharer")
|
||||
|
||||
@@ -396,7 +396,7 @@ public class BookingService {
|
||||
// book.setGuaranteeType("CGTD");
|
||||
dbBooking.setPaymentType(null);
|
||||
// book.setOperaProfileId(operaBooking.getOperaProfileId());
|
||||
dbBooking.setApproveStatus("N");
|
||||
dbBooking.setApproveStatus(daylightBooking.getApproveStatus());
|
||||
dbBooking.setCategory("DAYLIGHT");
|
||||
|
||||
dbBooking.setCreatedAt(DateTimeUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss"));
|
||||
|
||||
@@ -141,6 +141,8 @@ mcrm:
|
||||
default-send-to: 201249946@qq.com
|
||||
security:
|
||||
authorization-data: "[MCRM_APP/securItykeyOfCrmapp87895312],[MCRM_CMS/seCuritykeyOfCrmCms8wetw432]"
|
||||
daylight:
|
||||
booking-search-cache-seconds: 300
|
||||
server:
|
||||
port: 9087
|
||||
referrer-policy: origin-when-cross-origin
|
||||
@@ -179,6 +181,8 @@ apns:
|
||||
|
||||
daylight:
|
||||
scheduler:
|
||||
# Enable startup fetches and scheduled tasks. Restart after changes.
|
||||
open: false
|
||||
# Cron fields: second minute hour day-of-month month day-of-week. Restart after changes.
|
||||
configuration-cron: "0 0 2 * * ?"
|
||||
configuration-zone: "Asia/Shanghai"
|
||||
|
||||
Reference in New Issue
Block a user