diff --git a/pom.xml b/pom.xml
index f2ab9df..9743efd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,7 +20,7 @@
UTF-8
21
- 11.0.25
+ 11.0.26
4.2.18.Final
5.6.4
5.2.3
@@ -29,6 +29,7 @@
2.22.3
3.2.3
2.26.1
+ 9.2.0.Beta1
${java.version}
${java.version}
@@ -92,8 +93,12 @@
org.springframework.boot
spring-boot-starter-mail
-
+
+ jakarta.validation
+ jakarta.validation-api
+ 4.0.0-M1
+
org.springframework.boot
spring-boot-starter-validation
@@ -183,7 +188,7 @@
com.auth0
java-jwt
- 4.6.1
+ 4.6.2
com.eatthepath
diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/enums/EumnDaylightStatus.java b/src/main/java/com/mgm/mcrm/apigateway/common/enums/EumnDaylightStatus.java
index 781bd06..c0ed085 100644
--- a/src/main/java/com/mgm/mcrm/apigateway/common/enums/EumnDaylightStatus.java
+++ b/src/main/java/com/mgm/mcrm/apigateway/common/enums/EumnDaylightStatus.java
@@ -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;
diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java b/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java
index 1a9a66c..26ea03c 100644
--- a/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java
+++ b/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java
@@ -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 testFetchDaylightConfiguration() {
BaseApiResponse propertiesResponse = daylightFetchConfigurationService.fetchDaylightProperties();
if (!propertiesResponse.isSuccess()) {
diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/scheduler/DaylightConfigurationScheduler.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/scheduler/DaylightConfigurationScheduler.java
index 4405028..e1f3184 100644
--- a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/scheduler/DaylightConfigurationScheduler.java
+++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/scheduler/DaylightConfigurationScheduler.java
@@ -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 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 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 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 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 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());
diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightFetchConfigurationService.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightFetchConfigurationService.java
index 871dd40..893dd1e 100644
--- a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightFetchConfigurationService.java
+++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightFetchConfigurationService.java
@@ -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 fetchResourceManagementRequestors() {
+ BaseApiResponse 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 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 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 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 allResults = fetchResourceRequestorPages(
+ dlPropertyId, daylightApiToken.getAccessToken(), dateParams, response);
+ List oldItems = existingConfig != null
+ && StringUtils.isNotBlank(existingConfig.getConfigValue())
+ ? JSON.parseArray(existingConfig.getConfigValue(), JSONObject.class)
+ : new ArrayList<>();
+
+ // 所有页面成功后才保存,增量没有变化时保留原配置。
+ if (!isIncremental || !allResults.isEmpty()) {
+ List savedItems = allResults;
+ if (isIncremental) {
+ Map 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 fetchResourceRequestorPages(String dlPropertyId, String accessToken,
+ String dateParams, BaseApiResponse 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 allResults = new ArrayList<>();
+ int pageNum = 1;
+ Set 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 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 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;
+ }
}
diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/CfgDaylightConfigurationDao.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/CfgDaylightConfigurationDao.java
index 4dbf457..f3c7052 100644
--- a/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/CfgDaylightConfigurationDao.java
+++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/CfgDaylightConfigurationDao.java
@@ -17,7 +17,7 @@ public interface CfgDaylightConfigurationDao extends JpaRepository 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 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 ''")
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();
}
diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/services/DaylightApiService.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/services/DaylightApiService.java
index c92b572..3757847 100644
--- a/src/main/java/com/mgm/mcrm/apigateway/daylight/services/DaylightApiService.java
+++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/services/DaylightApiService.java
@@ -58,7 +58,7 @@ public class DaylightApiService {
BaseApiResponse 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")
diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ReservationCustomFieldsNotificationEvent.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ReservationCustomFieldsNotificationEvent.java
new file mode 100644
index 0000000..ccd625f
--- /dev/null
+++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ReservationCustomFieldsNotificationEvent.java
@@ -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 customFields;
+ }
+
+ @Data
+ public static class CustomField {
+ private String location;
+ private String value;
+ }
+}
diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ReservationHeaderCustomFields.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ReservationHeaderCustomFields.java
new file mode 100644
index 0000000..3c200a0
--- /dev/null
+++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ReservationHeaderCustomFields.java
@@ -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;
+}
diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationCustomFieldsNotificationHandler.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationCustomFieldsNotificationHandler.java
new file mode 100644
index 0000000..2d1c5ba
--- /dev/null
+++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationCustomFieldsNotificationHandler.java
@@ -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 handle(String payload, Map headers) throws InterruptedException {
+ BaseApiResponse 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 = 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;
+ }
+}
diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationNotificationHandler.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationNotificationHandler.java
index 2f76968..fa8ab58 100644
--- a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationNotificationHandler.java
+++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationNotificationHandler.java
@@ -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 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 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;
}
diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/models/SysBookingProfileMapping.java b/src/main/java/com/mgm/mcrm/apigateway/logging/models/SysBookingProfileMapping.java
new file mode 100644
index 0000000..f40ea3a
--- /dev/null
+++ b/src/main/java/com/mgm/mcrm/apigateway/logging/models/SysBookingProfileMapping.java
@@ -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;
+}
diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/repositories/SysBookingProfileMappingDao.java b/src/main/java/com/mgm/mcrm/apigateway/logging/repositories/SysBookingProfileMappingDao.java
new file mode 100644
index 0000000..edeed48
--- /dev/null
+++ b/src/main/java/com/mgm/mcrm/apigateway/logging/repositories/SysBookingProfileMappingDao.java
@@ -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 {
+ List findAllByCategoryAndPlayerId(String category, String playerId);
+
+ List findByProfileId(String profileId);
+}
+
diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/services/SysBookingCacheService.java b/src/main/java/com/mgm/mcrm/apigateway/logging/services/SysBookingCacheService.java
new file mode 100644
index 0000000..22c9569
--- /dev/null
+++ b/src/main/java/com/mgm/mcrm/apigateway/logging/services/SysBookingCacheService.java
@@ -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 result = sysBookingProfileMappingDao.findByProfileId(profileId);
+
+ if (CollectionUtils.isEmpty(result)) {
+ return null;
+ }
+
+ return result.getFirst();
+ }
+}
diff --git a/src/main/java/com/mgm/mcrm/apigateway/reservation/models/Bookings.java b/src/main/java/com/mgm/mcrm/apigateway/reservation/models/Bookings.java
index 23ca4d4..ad695f4 100644
--- a/src/main/java/com/mgm/mcrm/apigateway/reservation/models/Bookings.java
+++ b/src/main/java/com/mgm/mcrm/apigateway/reservation/models/Bookings.java
@@ -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")
diff --git a/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java b/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java
index 473f9fc..856b4f9 100644
--- a/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java
+++ b/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java
@@ -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"));
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index dab80a2..e2f4a36 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -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"