From 3b6f0495a3e48e08d4f384dc2432046dedfa9f8b Mon Sep 17 00:00:00 2001 From: Mauve Date: Mon, 28 Sep 2026 10:28:04 +0800 Subject: [PATCH] update --- crm_mobile_runtime/appconfig.properties | 20 +- .../convert/bin/BookingService$1.class | Bin 7513 -> 0 bytes .../convert/bin/BookingService.class | Bin 135728 -> 0 bytes pom.xml | 97 ++- .../com/mgm/mcrm/CRMMobileApplication.java | 7 +- .../controllers/SSORestProcessController.java | 55 +- .../repositories/ApprovalRoutingDao.java | 12 - .../approval/service/ApprovalService.java | 87 ++- .../controllers/CfgSettingController.java | 14 +- .../controllers/ConfigController.java | 24 +- .../appsetting/services/ConfigService.java | 17 +- .../controllers/MobileLoginController.java | 20 +- .../services/MgmUserService.java | 10 +- .../daylight/models/CfgMarketCodes.java | 3 + .../daylight/models/CfgRateCodes.java | 3 + .../daylight/models/CfgSourceCodes.java | 5 +- .../basesetting/daylight/models/CfgTiers.java | 3 + .../daylight/repositories/RateCodeDao.java | 3 - .../daylight/repositories/SourceCodeDao.java | 1 + .../daylight/repositories/TierDao.java | 4 +- .../services/CompRuleService.java.bak | 351 ++++++++++ .../daylight/services/DaylightApiService.java | 75 +- .../property/repositories/PropertyDao.java | 3 + .../basesetting/room/models/CfgRoomTypes.java | 3 + .../room/repositories/RoomTypeDao.java | 2 +- .../controllers/BookingController.java | 26 +- .../controllers/WaitlistController.java | 6 +- .../com/mgm/mcrm/booking/dtos/BookingDto.java | 27 +- .../createReservation/IndividualDTO.java | 45 +- .../mcrm/booking/mappers/BookingMapper.java | 3 + .../mcrm/booking/models/OperaDBBooking.java | 150 +--- .../mcrm/booking/repositories/BookingDao.java | 7 +- .../mcrm/booking/services/BookingService.java | 639 ++++++++++++++---- .../services/NonMGMBookingService.java | 4 +- .../services/OperaDBBookingService.java | 5 + .../mcrm/booking/services/OperaService.java | 107 +-- .../services/WaitlistBookingService.java | 70 +- .../mcrm/common/config/ApiGatewayConfig.java | 125 ++++ .../common/config/MGMDataSourceConfig.java | 10 +- .../common/config/OperaDataSourceConfig.java | 10 +- .../mgm/mcrm/common/constants/SysCommon.java | 51 +- .../mcrm/common/dtos/config/APIConfig.java | 46 -- .../common/dtos/config/ApiGatewaySetting.java | 32 + .../common/dtos/mgm_api/ApproverRecord.java | 19 +- .../dtos/mgm_api/ApproverRecordResult.java | 109 +-- .../common/dtos/mgm_api/ApproverResult.java | 23 +- .../dtos/mgm_api/TodoDateListRecord.java | 6 +- .../dtos/mgm_api/TodoDateListResult.java | 6 +- .../common/dtos/mgm_api/TodoListRecord.java | 187 +++-- .../common/filters/ConfigHeaderFilter.java | 34 +- .../common/schedule/BookingScheduleJob.java | 2 +- .../mgm/mcrm/common/services/HttpService.java | 53 +- .../mcrm/common/services/MGMApiService.java | 468 ------------- .../common/services/MGMEsbApiService.java | 483 ++++++++----- .../common/services/MGMEsbLimoApiService.java | 91 +++ .../common/services/MGMLimoApiService.java | 77 +-- .../mgm/mcrm/common/utils/EncryptUtil.java | 49 +- .../com/mgm/mcrm/common/utils/ExcelUtils.java | 39 +- .../com/mgm/mcrm/common/utils/HttpUtil.java | 25 +- .../mgm/mcrm/common/utils/NetworkUtil.java | 156 ++++- .../com/mgm/mcrm/common/utils/SOAPUtil.java | 56 +- .../com/mgm/mcrm/common/utils/SendEmail.java | 172 ++++- .../mcrm/limo/controller/LimoController.java | 16 +- .../mgm/mcrm/limo/services/LimoService.java | 51 +- .../controllers/SysLogAppController.java | 6 +- .../controllers/SysLogConfirmController.java | 4 +- .../controllers/EmailTemplateController.java | 6 +- .../controllers/NotificationController.java | 10 +- .../dtos/request/SendNotificationRequest.java | 2 + .../services/NotificationService.java | 9 +- .../player/controllers/PlayerController.java | 14 +- .../player/controllers/TodoController.java | 15 +- .../dtos/request/TodoChangeDateRequest.java | 2 + .../mcrm/player/services/PlayerService.java | 12 +- .../mgm/mcrm/player/services/TodoService.java | 169 ++--- .../services/RoomCalendarService.java | 10 +- src/main/resources/application-dev.yml | 48 +- src/main/resources/log4j2-spring.xml | 41 ++ src/main/resources/logback.xml | 60 -- .../resources/mockup/app/getTodoList_4.json | 4 +- .../mgm/daylight/EnumBookingStatusTest.java | 21 - .../mgm/daylight/HttpEntityBuilderTest.java | 29 - src/test/java/com/mgm/daylight/SmallTest.java | 19 - .../mcrm/PublicEndpointsMigrationTest.java | 129 ---- .../mgm/mcrm/RefreshTokenMigrationTest.java | 109 --- .../com/mgm/mcrm/TodoIrcMigrationTest.java | 22 - .../mgm/mcrm/TodoNumericMigrationTest.java | 56 -- .../mgm/mcrm/TodoRemainingContractTest.java | 68 -- .../BookingControllerRecordSearchTest.java | 61 -- .../CalendarBootstrapMigrationTest.java | 143 ---- .../roomcalendar/CalendarMockDataTest.java | 76 --- .../CalendarQueryMigrationTest.java | 254 ------- .../crm_mobile_runtime/appconfig.properties | 16 - .../import/import_other_booking.json | 197 ------ 94 files changed, 2941 insertions(+), 2975 deletions(-) delete mode 100644 crm_mobile_runtime/convert/bin/BookingService$1.class delete mode 100644 crm_mobile_runtime/convert/bin/BookingService.class create mode 100644 src/main/java/com/mgm/mcrm/basesetting/daylight/services/CompRuleService.java.bak create mode 100644 src/main/java/com/mgm/mcrm/common/config/ApiGatewayConfig.java create mode 100644 src/main/java/com/mgm/mcrm/common/dtos/config/ApiGatewaySetting.java delete mode 100644 src/main/java/com/mgm/mcrm/common/services/MGMApiService.java create mode 100644 src/main/java/com/mgm/mcrm/common/services/MGMEsbLimoApiService.java create mode 100644 src/main/resources/log4j2-spring.xml delete mode 100644 src/main/resources/logback.xml delete mode 100644 src/test/java/com/mgm/daylight/EnumBookingStatusTest.java delete mode 100644 src/test/java/com/mgm/daylight/HttpEntityBuilderTest.java delete mode 100644 src/test/java/com/mgm/daylight/SmallTest.java delete mode 100644 src/test/java/com/mgm/mcrm/PublicEndpointsMigrationTest.java delete mode 100644 src/test/java/com/mgm/mcrm/RefreshTokenMigrationTest.java delete mode 100644 src/test/java/com/mgm/mcrm/TodoIrcMigrationTest.java delete mode 100644 src/test/java/com/mgm/mcrm/TodoNumericMigrationTest.java delete mode 100644 src/test/java/com/mgm/mcrm/TodoRemainingContractTest.java delete mode 100644 src/test/java/com/mgm/mcrm/booking/controllers/BookingControllerRecordSearchTest.java delete mode 100644 src/test/java/com/mgm/mcrm/roomcalendar/CalendarBootstrapMigrationTest.java delete mode 100644 src/test/java/com/mgm/mcrm/roomcalendar/CalendarMockDataTest.java delete mode 100644 src/test/java/com/mgm/mcrm/roomcalendar/CalendarQueryMigrationTest.java delete mode 100644 target/crm_mobile_runtime/appconfig.properties delete mode 100644 target/crm_mobile_runtime/excel_define/import/import_other_booking.json diff --git a/crm_mobile_runtime/appconfig.properties b/crm_mobile_runtime/appconfig.properties index 2f2b56e..a5188ec 100644 --- a/crm_mobile_runtime/appconfig.properties +++ b/crm_mobile_runtime/appconfig.properties @@ -1,21 +1,23 @@ #ESB Setting esb_app_id=mgmtestuser001 esb_app_secret=MGM@!666 -esb_server=https://mmesbdevapp14/api/crmapp/v1.0 +#esb_server=https://mmesbdevapp14/api/crmapp/v1.0 +esb_server=https://dlhcadev.mcmsapoc.com/api/esb/proxy esb_request_timeout=30000 #OWS Setting -ows_server=https://mgpmsdevows45 -ows_request_timeout=30000 +#ows_server=https://mgpmsdevows45 +ows_server=https://dlhcadev.mcmsapoc.com/api/opera/proxy +ows_request_timeout=60000 #ESB Limo Setting -esb_limo_server=https://mmesbdevapp14/api/limo/v1.0 +esb_limo_server=https://dlhcadev.mcmsapoc.com/api/esb_limo/v1.0 esb_limo_app_id=Limotest esb_limo_app_key=Limotest123 #SMTP setting -mail_port=25 -mail_smtp_host=smtp.aliyun.com -mail_sender=linp168@aliyun.com -mail_sender_password=linpeng060504 -mail_send_to=1441170195@qq.com;feng.cai@willtechmacau.com \ No newline at end of file +mail_port=587 +mail_smtp_host=smtp.qq.com +mail_sender=201249946@qq.com +mail_sender_password=oheejklduyydbife +mail_send_to=mauve.chan@willtechmacau.com \ No newline at end of file diff --git a/crm_mobile_runtime/convert/bin/BookingService$1.class b/crm_mobile_runtime/convert/bin/BookingService$1.class deleted file mode 100644 index 39d5a8b306431aec43a2c748d1a3cd6280ac9c41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7513 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 AX8-^I diff --git a/crm_mobile_runtime/convert/bin/BookingService.class b/crm_mobile_runtime/convert/bin/BookingService.class deleted file mode 100644 index abc5f29233f8b10c2965232cbc5f44496d4f7f2f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 135728 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
-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 Do^Sv? diff --git a/pom.xml b/pom.xml index 6714486..ee28e21 100644 --- a/pom.xml +++ b/pom.xml @@ -13,26 +13,80 @@ org.springframework.boot spring-boot-starter-parent - 3.5.13 + 4.2.0-M1 - 21 - 1.5.25 - 6.3.1 - 2.21.2 - 10.1.54 - 6.2.18 + UTF-8 + 21 + 4.2.18.Final + 5.6.4 + 5.2.3 + 5.4.3 + 6.2.7 + 2.22.3 + 3.2.3 + 2.26.1 + ${java.version} + ${java.version} + + + + + io.netty + netty-bom + ${netty.version} + pom + import + + + org.apache.httpcomponents.core5 + httpcore5-h2 + ${httpcore5.version} + + + com.fasterxml.jackson + jackson-bom + ${fasterxml.jackson.version} + pom + import + + + tools.jackson + jackson-bom + ${tools.jackson.version} + pom + import + + + + org.springframework.boot spring-boot-starter + + + org.springframework.boot + spring-boot-starter-logging + + org.springframework.boot spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j2 org.springframework.boot @@ -49,7 +103,7 @@ org.apache.activemq activemq-client - 6.2.4 + ${activemq.version} org.springframework.boot @@ -59,6 +113,8 @@ org.springframework.boot spring-boot-starter-aop + 4.0.0-M2 + compile org.springframework.boot @@ -73,16 +129,16 @@ com.microsoft.sqlserver mssql-jdbc - - org.apache.httpcomponents.client5 - httpclient5 - 5.5 - - - org.apache.httpcomponents.client5 - httpclient5-win - 5.2.1 - + + org.apache.httpcomponents.client5 + httpclient5 + ${httpclient5.version} + + + org.apache.httpcomponents.client5 + httpclient5-win + ${httpclient5-win.version} + org.springframework.boot spring-boot-starter-tomcat @@ -130,11 +186,6 @@ commons-text 1.14.0 - - org.jsoup - jsoup - 1.23.1 - org.springframework.boot spring-boot-devtools diff --git a/src/main/java/com/mgm/mcrm/CRMMobileApplication.java b/src/main/java/com/mgm/mcrm/CRMMobileApplication.java index 823b750..f3af859 100644 --- a/src/main/java/com/mgm/mcrm/CRMMobileApplication.java +++ b/src/main/java/com/mgm/mcrm/CRMMobileApplication.java @@ -2,8 +2,8 @@ package com.mgm.mcrm; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.domain.EntityScan; -import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.boot.persistence.autoconfigure.EntityScan; +import org.springframework.boot.web.server.servlet.context.ServletComponentScan; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.ComponentScan; import org.springframework.jms.annotation.EnableJms; @@ -16,11 +16,10 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; @ComponentScan(basePackages="com.mgm.mcrm") @EntityScan(basePackages="com.mgm.mcrm") @EnableTransactionManagement -@ServletComponentScan +@ServletComponentScan @EnableScheduling @EnableAsync @EnableJms -@EnableCaching public class CRMMobileApplication{ public static void main( String[] args ) { SpringApplication.run(CRMMobileApplication.class, args); diff --git a/src/main/java/com/mgm/mcrm/adfs/controllers/SSORestProcessController.java b/src/main/java/com/mgm/mcrm/adfs/controllers/SSORestProcessController.java index d3bf33c..705c8d3 100644 --- a/src/main/java/com/mgm/mcrm/adfs/controllers/SSORestProcessController.java +++ b/src/main/java/com/mgm/mcrm/adfs/controllers/SSORestProcessController.java @@ -86,9 +86,11 @@ public class SSORestProcessController { private boolean checkValidUrl(String requestUrl){ boolean isValid = NetworkUtil.isValidURL(requestUrl); - if(isValid){ - if(!requestUrl.startsWith(loginCallbackUrl) + if(isValid){ + String callbackHost = loginCallbackUrl.replace("/sso_callback/login",""); + if(!requestUrl.startsWith(callbackHost) && !requestUrl.startsWith(adfsHost)){ + LogUtil.info(SSORestProcessController.class,"Failed validate of valid url["+callbackHost+"]["+adfsHost+"]"); isValid =false; } } @@ -128,7 +130,21 @@ public class SSORestProcessController { String paraRelying = StringUtils.isNotBlank(request.getParameter("relyingParty"))?request.getParameter("relyingParty"):relyingParty; String authorizedUrl = adfsHost+ "/adfs/ls/IdpInitiatedSignOn.aspx?loginToRp="+paraRelying; String requestClientId = request.getParameter("client_request_id"); - Map cookieValues = new HashMap(); + Map cookieValues = new HashMap();String origin = request.getHeader("Origin"); + LogUtil.info(SSORestProcessController.class,"URI:"+request.getRequestURI()+",Origin:"+origin); +// +// String origin = request.getHeader("Origin"); + String sanitizedOrigin = NetworkUtil.sanitizeCRLF(origin); + if(SysCommon.isAllowOriginSource(sanitizedOrigin)) { + response.setHeader("Access-Control-Allow-Origin", NetworkUtil.sanitizeCRLF(sanitizedOrigin)); + }else { + LogUtil.warn(SSORestProcessController.class, "Origin of ["+origin+"] is not allowed"); + response.setHeader("Access-Control-Allow-Origin",""); + // + String errorMessage = HtmlUtils.htmlEscape(("Origin of ["+origin+"] is not allowed")); + response.sendError(401, errorMessage); + } + if(StringUtils.isBlank(requestClientId)) { requestClientId = UUID.randomUUID().toString(); } @@ -182,6 +198,7 @@ public class SSORestProcessController { String redirectUrl = loginCallbackUrl+"?"; redirectUrl+=("error="+URLEncoder.encode(errorCode,"UTF-8")); redirectUrl+=("&errorMessage="+URLEncoder.encode(errorMessage,"UTF-8")); + response.setHeader("Origin","MGM_CRM_APP"); response.sendRedirect(redirectUrl); }else { CookieStore cookieStore = new BasicCookieStore(); @@ -242,7 +259,7 @@ public class SSORestProcessController { for(String cookieKey:cookieKeys) { try { String cookieVal = cookieValues.get(cookieKey); - LogUtil.info(SSORestProcessController.class,"Set Cookie ["+cookieKey+"]="+cookieVal); + //LogUtil.info(SSORestProcessController.class,"Set Cookie ["+cookieKey+"]="+cookieVal); if(cookieSetting!=null && !cookieSetting.isEmpty()) { Iterator cookieSetKeys = cookieSetting.keySet().iterator(); while(cookieSetKeys.hasNext()) { @@ -253,7 +270,7 @@ public class SSORestProcessController { cookieDomain=""; } for(String cookiePath:cookiePaths) { - BasicClientCookie cookie = new BasicClientCookie(cookieKey, cookieVal); + BasicClientCookie cookie = new BasicClientCookie(NetworkUtil.sanitizeCRLF(cookieKey), NetworkUtil.sanitizeCRLF(cookieVal)); try { Date currentDate = new Date(); Date expiryDate = new Date(currentDate.getTime() + 1800 * 1000); @@ -261,8 +278,8 @@ public class SSORestProcessController { }catch(Exception ignored) { } - cookie.setPath(cookiePath); - cookie.setDomain(cookieDomain); + cookie.setPath(NetworkUtil.sanitizeCRLF(cookiePath)); + cookie.setDomain(NetworkUtil.sanitizeCRLF(cookieDomain)); cookie.setSecure(true); cookie.setAttribute("httponly", "true"); cookieStore.addCookie(cookie); @@ -363,7 +380,7 @@ public class SSORestProcessController { extraHeaders.forEach((key, value) -> { //LogUtil.info(SSORestProcessController.class,"Expend Header:"+key+" Value:"+value); if(!"content-type".equalsIgnoreCase(key)){ - request.getHeaders().add(key, NetworkUtil.sanitizeHeaderValue(value)); + request.getHeaders().add(key, NetworkUtil.sanitizeCRLF(value)); } }); } @@ -524,7 +541,7 @@ public class SSORestProcessController { LogUtil.info(SSORestProcessController.class,"Read Response Body Failed:"+err.getMessage()); } String responseBody = stringBuilder.toString(); - LogUtil.info(SSORestProcessController.class,"Response 200:["+responseBody+"]"); + //LogUtil.info(SSORestProcessController.class,"Response 200:["+responseBody+"]"); responseBody = responseBody.replace("", "\n"); responseBody = responseBody.replace("//", ""); @@ -614,7 +631,7 @@ public class SSORestProcessController { resHeaders.forEach((key, value) -> { try { //LogUtil.info(SSORestProcessController.class,"Res Header:"+key+" Value:"+value); - response.setHeader(key, NetworkUtil.sanitizeHeaderValue(value)); + response.setHeader(key, NetworkUtil.sanitizeCRLF(value)); }catch(Exception ignored) { } @@ -628,34 +645,36 @@ public class SSORestProcessController { response.setHeader("Content-Length", Long.toString(contentLength)); StreamUtils.copy(outputData, response.getOutputStream()); }else if(Strings.CS.equals(nextStep, "Redirect")){ - String redirectUrl = nextUrl; + String redirectUrl = NetworkUtil.validateAndSanitizeUrl(nextUrl); if(!checkValidUrl(redirectUrl)){ throw new Exception("Invalid redirect url:"+redirectUrl); } if(StringUtils.isBlank(response.getHeader("Location"))) { LogUtil.info(SSORestProcessController.class,"Redirect location:"+redirectUrl); - response.setHeader("Location", NetworkUtil.sanitizeHeaderValue(redirectUrl)); + response.setHeader("Location", NetworkUtil.sanitizeCRLF(redirectUrl)); } response.sendRedirect(redirectUrl); }else { String redirectUrl = callbackUrl+"?"; - redirectUrl+=("errorCode="+URLEncoder.encode(errorCode,"UTF-8")); - redirectUrl+=("&errorMessage="+URLEncoder.encode(errorMessage,"UTF-8")); + redirectUrl+=("errorCode="+URLEncoder.encode(errorCode, StandardCharsets.UTF_8)); + redirectUrl+=("&errorMessage="+URLEncoder.encode(errorMessage, StandardCharsets.UTF_8)); if(!checkValidUrl(redirectUrl)){ throw new Exception("Invalid redirect url:"+redirectUrl); } - response.setHeader("Location", NetworkUtil.sanitizeHeaderValue(redirectUrl)); + String saintUrl = NetworkUtil.validateAndSanitizeUrl(redirectUrl); + response.setHeader("Location", NetworkUtil.sanitizeCRLF(saintUrl)); response.setHeader("Content-Length","0"); - LogUtil.info(SSORestProcessController.class,"Callback with ErrorCode:"+redirectUrl+" [errorMessage]:"+errorMessage); - response.sendRedirect(redirectUrl); + LogUtil.info(SSORestProcessController.class,"Callback with ErrorCode:"+saintUrl+" [errorMessage]:"+errorMessage); + response.sendRedirect(saintUrl); } }else { String redirectUrl = callbackUrl+"?errorCode=Invalid_Result&errorMessage=Invalid_Result"; if(!checkValidUrl(redirectUrl)){ throw new Exception("Invalid redirect url:"+redirectUrl); } - response.setHeader("Location", NetworkUtil.sanitizeHeaderValue(redirectUrl)); + response.setHeader("Location", NetworkUtil.sanitizeCRLF(redirectUrl)); response.setHeader("Content-Length","0"); + response.setHeader("Origin","MGM_CRM_APP"); response.sendRedirect(redirectUrl); } } diff --git a/src/main/java/com/mgm/mcrm/approval/repositories/ApprovalRoutingDao.java b/src/main/java/com/mgm/mcrm/approval/repositories/ApprovalRoutingDao.java index de04410..7788629 100644 --- a/src/main/java/com/mgm/mcrm/approval/repositories/ApprovalRoutingDao.java +++ b/src/main/java/com/mgm/mcrm/approval/repositories/ApprovalRoutingDao.java @@ -29,21 +29,9 @@ public interface ApprovalRoutingDao extends JpaRepository findByBookingStatus(String bookingStatus); - - Integer countByBookingStatus(String bookingStatus); - - Integer countByBookingStatusAndApproverId(String bookingStatus, String approverId); CfgApprovalRouting getByBookingId(Long bookingId); - List findByBookingStatusAndApproverId(String ap, String userId); - @Query(value = "select * from cfg_approval_routing where booking_status = 'AP' and approver_id like concat('%', ?1, '%')", nativeQuery = true) List getApprovalList(String userId); diff --git a/src/main/java/com/mgm/mcrm/approval/service/ApprovalService.java b/src/main/java/com/mgm/mcrm/approval/service/ApprovalService.java index 6303f98..42903c7 100644 --- a/src/main/java/com/mgm/mcrm/approval/service/ApprovalService.java +++ b/src/main/java/com/mgm/mcrm/approval/service/ApprovalService.java @@ -50,11 +50,12 @@ import com.mgm.mcrm.common.dtos.BaseApiResponse; import com.mgm.mcrm.common.dtos.EumnApprovalStatus; import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; import com.mgm.mcrm.common.dtos.mgm_api.ApprovalGamingDataResult; +import com.mgm.mcrm.common.dtos.mgm_api.ApproverRecordResult; import com.mgm.mcrm.common.dtos.mgm_api.ApproverResult; import com.mgm.mcrm.common.dtos.mgm_api.PlayerProfileEntry; import com.mgm.mcrm.common.dtos.mgm_api.UserInfoApiRecord; import com.mgm.mcrm.common.jms.BookingControlMsgSender; -import com.mgm.mcrm.common.services.MGMApiService; +import com.mgm.mcrm.common.services.MGMEsbApiService; import com.mgm.mcrm.common.utils.DateTimeUtil; import com.mgm.mcrm.common.utils.LogUtil; import com.mgm.mcrm.common.utils.SendEmail; @@ -63,6 +64,7 @@ import com.mgm.mcrm.logging.repositories.SysLogBookingDao; import com.mgm.mcrm.logging.services.SysLogBookingService; import com.mgm.mcrm.notification.models.Email; import com.mgm.mcrm.notification.repositories.EmailDao; +import com.mgm.mcrm.player.dtos.request.TodoChangeDateRequest; import com.mgm.mcrm.player.services.PlayerService; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; @@ -100,7 +102,7 @@ public class ApprovalService { @Resource private RequestReasonDao requestReasonDao; @Resource - private MGMApiService mgmApiService; + private MGMEsbApiService mgmEsbApiService; @Resource private PropertyDao propertyDao; @Resource @@ -188,9 +190,11 @@ public class ApprovalService { } try { - HttpResponseResult httpResult = mgmApiService.invokeGetApprover(playerId,bookingType,propertyName,roomCategory,checkInDate,checkOutDate,userId); + HttpResponseResult httpResult = mgmEsbApiService.invokeGetApprover(playerId,bookingType,propertyName,roomCategory,checkInDate,checkOutDate,userId); try { + JSONObject rawResult = JSONObject.parseObject(httpResult.getResponseContent()); ApproverResult resultObj = JSONObject.parseObject(httpResult.getResponseContent(), ApproverResult.class, Feature.SupportNonPublicField,Feature.IgnoreNotMatch); + fillApproverListFromRawJson(resultObj, rawResult); if(Strings.CS.equals(resultObj.getStatusCode(), "OK")) { if (resultObj.getRecords() != null && !resultObj.getRecords().isEmpty()) { response.setSuccess(true); @@ -222,11 +226,39 @@ public class ApprovalService { return response; } + private void fillApproverListFromRawJson(ApproverResult resultObj, JSONObject rawResult) { + if (resultObj == null || resultObj.getRecords() == null || rawResult == null) { + return; + } + JSONArray rawRecords = rawResult.getJSONArray("Records"); + if (rawRecords == null) { + return; + } + List records = resultObj.getRecords(); + int size = Math.min(records.size(), rawRecords.size()); + for (int i = 0; i < size; i++) { + JSONObject rawRecord = rawRecords.getJSONObject(i); + if (rawRecord == null) { + continue; + } + records.get(i).setApproverList(rawRecord.get("ApproverList")); + } + } + public BaseApiResponse getApprovalData(String bookingId) throws Exception { BaseApiResponse response = new BaseApiResponse<>(); - ApprovalGamingDataResult approvalGamingData = new ApprovalGamingDataResult(); + ApprovalGamingDataResult approvalGamingData; try { - HttpResponseResult httpResult = mgmApiService.invokeGetApprovalData(bookingId); +// String bookingCategory = bookingDao.findCategoryById(Long.valueOf(bookingId)); + +// HttpResponseResult httpResult; +// if ("DAYLIGHT".equalsIgnoreCase(bookingCategory)) { +// httpResult = mgmApiService.invokeGetApprovalData(bookingId, 1); +// } else { +// httpResult = mgmApiService.invokeGetApprovalData(bookingId, 0); +// } + HttpResponseResult httpResult = mgmEsbApiService.invokeGetApprovalData(bookingId, 1); + try { approvalGamingData = JSONObject.parseObject(httpResult.getResponseContent(), ApprovalGamingDataResult.class, Feature.SupportNonPublicField,Feature.IgnoreNotMatch); if(StringUtils.equalsIgnoreCase(approvalGamingData.getStatusCode(),"OK")) { @@ -722,7 +754,7 @@ public class ApprovalService { LogUtil.info(ApprovalService.class,"Submit Approval.UserId:"+userId); //相同 处理approvalRouting记录 并调用 Call OWS createbooking approvalRouting.setBookingStatus("AA"); - CfgApprovalRouting changeRecord = approvalRoutingDao.save(approvalRouting); + approvalRoutingDao.save(approvalRouting); //处理Pending Approval Booking 记录 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); @@ -791,8 +823,14 @@ public class ApprovalService { // 获取Property CfgProperties properties = propertyDao.findById(originBooking.getPropertyId()).orElse(null); - Boolean isOverBook = this.checkIsOverbook(bookingView.getRoomId(), bookingView.getCheckinDate(), - bookingView.getCheckoutDate(), bookingView.getPropertyId(), bookingView.getBlockId()); + Boolean isOverBook; + if ("DAYLIGHT".equalsIgnoreCase(bookingView.getPropertyCategory())) { + isOverBook = false; + } else { + isOverBook = this.checkIsOverbook(bookingView.getRoomId(), bookingView.getCheckinDate(), + bookingView.getCheckoutDate(), bookingView.getPropertyId(), bookingView.getBlockId()); + } + Boolean isOtherOverBook = this.checkOtherIsOverbook(Math.toIntExact(originBooking.getRoomId()), originBooking.getCheckoutDate(), originBooking.getCheckinDate(), originBooking.getPropertyId(), originBooking.getSegmentId()); @@ -812,6 +850,7 @@ public class ApprovalService { // BookingsView 中 roomId/blockId 为 Long,Bookings 为 Integer,copyProperties 会跳过;View 无 bookingTypeId newBooking.setApprovalBookingId(String.valueOf(originBooking.getId())); + newBooking.setCategory("OPERA"); if (originBookingView.getRoomId() != null) { newBooking.setRoomId(originBookingView.getRoomId()); @@ -850,6 +889,7 @@ public class ApprovalService { newBooking.setApprovalBookingId(String.valueOf(originBooking.getId())); newBooking.setDateList(originBooking.getDateList()); newBooking.setMember(originBooking.getMember()); + newBooking.setCategory("DAYLIGHT"); if (originBookingView.getRoomId() != null) { newBooking.setRoomId(originBookingView.getRoomId()); @@ -889,6 +929,7 @@ public class ApprovalService { email.setBookId(originBooking.getId()); email.setCreatedAt(creatdDate); email.setCreatedBy(originBooking.getCreatedBy()); + emailBook.setCategory("OTHER"); email = SendEmail.sendApprovalNewEmail(emailBook, originBookingView,email,userId,userName,true,false,emailAddress.getKeyValues()); if(email.getStatus()==1) { originBooking.setApproveStatus("AA"); @@ -905,6 +946,7 @@ public class ApprovalService { newBooking.setLastOperaStatus("PENDING"); newBooking.setApprovedBy(userName); newBooking.setApprovedId(Long.valueOf(userId)); + newBooking.setCategory("OTHER"); //获取other room status CfgOtherRoomStatus roomStatus = roomStatusDao.findByStatusName("Pending"); if(roomStatus!=null) { @@ -960,6 +1002,35 @@ public class ApprovalService { response.setLogMessage("Send Email Failed"); response.setMessage("Send Email Failed"); } + }else if (Strings.CS.equals(properties.getCategory(),"DAYLIGHT")) { + //调用daylight接口去Extend Booking + BaseApiResponse findPlayerResult = playerService.findPlayerProfileByPlayerIdAndUserId(originBooking.getPlayerId(), userId); + if(findPlayerResult.isSuccess()) { + originBooking.setApproveStatus("AA"); + bookingDao.save(originBooking); + }else{ + response.setSuccess(false); + response.setCode("Player Profile Not Found"); + } + //Directly send email when submit Approval Routing for daylight + BookingsView originBookingView = bookingViewDao.findById(Long.parseLong(originBooking.getApprovalBookingId())); + originBookingView = this.getEmailInfo(originBookingView,originBookingView.getCreatedBy()).getData(); + Email email = new Email(); + + email.setBookId(originBooking.getId()); + email.setCreatedAt(creatdDate); + email.setCreatedBy(originBooking.getCreatedBy()); + + email = SendEmail.sendApprovalExtendEmail(originBooking, originBookingView,email,userId,userName,bookingView,isOtherOverBook,emailAddress.getKeyValues()); + if(email.getStatus()==1) { + emailDao.save(email); + response.setSuccess(true); + response.setData(approvalMapper.toSubmitApprovalResponse(originBooking)); + }else{ + response.setSuccess(false); + response.setMessage("Send Email Failed"); + response.setLogMessage("Send Email Failed"); + } }else{ //send email instead of sync other booking BookingsView originBookingView = bookingViewDao.findById(Long.parseLong(originBooking.getApprovalBookingId())); diff --git a/src/main/java/com/mgm/mcrm/appsetting/controllers/CfgSettingController.java b/src/main/java/com/mgm/mcrm/appsetting/controllers/CfgSettingController.java index 837a3d4..d191477 100644 --- a/src/main/java/com/mgm/mcrm/appsetting/controllers/CfgSettingController.java +++ b/src/main/java/com/mgm/mcrm/appsetting/controllers/CfgSettingController.java @@ -12,6 +12,7 @@ import com.mgm.mcrm.common.utils.NetworkUtil; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -20,7 +21,6 @@ import java.util.List; @RestController -@CrossOrigin(origins = "*") @RequestMapping("/api") public class CfgSettingController { private static final List PUBLIC_LOGIN_SETTING_KEYS = List.of("APP_MOCK_SSO", "APP_MOCK_TPD"); @@ -30,12 +30,12 @@ public class CfgSettingController { @Resource private SysSettingService sysSettingService; - @PostMapping(value = "/app/setting/get_calendar_setting", produces = "application/json;charset=UTF-8") + @PostMapping(value = "/app/setting/get_calendar_setting", produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> getCalendarSetting() { return ResponseEntity.ok(BaseApiResponse.ok("Get Setting Successed", settingService.getCalendarSetting())); } - @RequestMapping(value = "/web/setting/get_setting_by_key", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/setting/get_setting_by_key", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getSettingByKey(HttpServletRequest request, @RequestBody CfgSettingQueryRequest params) throws Exception { boolean isSuccess = false; String processMessage=""; @@ -57,7 +57,7 @@ public class CfgSettingController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @PostMapping(value = "/web/setting/get_public_setting_by_key", produces = "application/json;charset=UTF-8") + @PostMapping(value = "/web/setting/get_public_setting_by_key", produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getPublicSettingByKey(HttpServletRequest request, @RequestBody(required=false) CfgSettingQueryRequest params) throws Exception { if (params == null || !"MACAU_STRING".equals(params.getKeyName())) { @@ -66,7 +66,7 @@ public class CfgSettingController { return getSettingByKey(request, params); } - @PostMapping(value = "/system/sys_setting/get_public_login_settings", produces = "application/json;charset=UTF-8") + @PostMapping(value = "/system/sys_setting/get_public_login_settings", produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> getPublicLoginSettings(HttpServletRequest request) { boolean internalNetwork = NetworkUtil.isMatchInternal(NetworkUtil.getClientIpAddr(request)); List settings = new ArrayList<>(); @@ -80,7 +80,7 @@ public class CfgSettingController { return ResponseEntity.ok(BaseApiResponse.ok("", settings)); } - @RequestMapping(value = "/system/sys_setting/get", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/system/sys_setting/get", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getSysSettingByKey(HttpServletRequest request, @RequestBody SysSettingQueryRequest queryFilter) { BaseApiResponse result = new BaseApiResponse<>(); //-- @@ -99,7 +99,7 @@ public class CfgSettingController { } return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/system/sys_setting/get_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/system/sys_setting/get_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> getSysSettings(HttpServletRequest request, @RequestBody(required=false) SysSettingQueryRequest queryFilter) { boolean isSuccess = false; String processMessage=""; diff --git a/src/main/java/com/mgm/mcrm/appsetting/controllers/ConfigController.java b/src/main/java/com/mgm/mcrm/appsetting/controllers/ConfigController.java index 27e69fc..053ca40 100644 --- a/src/main/java/com/mgm/mcrm/appsetting/controllers/ConfigController.java +++ b/src/main/java/com/mgm/mcrm/appsetting/controllers/ConfigController.java @@ -16,6 +16,7 @@ import com.mgm.mcrm.common.utils.LogUtil; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -24,14 +25,13 @@ import java.util.List; import java.util.Map; @RestController -@CrossOrigin(origins = "*") @RequestMapping("/api") public class ConfigController { @Resource private ConfigService configService; - @RequestMapping(value = "/app/config/property_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/config/property_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> queryCfgPropertys(HttpServletRequest request, @RequestBody(required=true) PropertyQueryRequest queryFilter) { BaseApiResponse> processResult = new BaseApiResponse<>(); //-- @@ -42,7 +42,7 @@ public class ConfigController { return ResponseEntity.status(HttpStatus.OK).body(processResult); } - @RequestMapping(value = "/app/config/room_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/config/room_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> queryRoom(HttpServletRequest request, @RequestBody(required=true) RoomQueryRequest queryFilter) { BaseApiResponse> processResult = new BaseApiResponse<>(); List listCfgRooms = configService.listRoom(queryFilter); @@ -52,7 +52,7 @@ public class ConfigController { return ResponseEntity.status(HttpStatus.OK).body(processResult); } - @RequestMapping(value = "/app/config/room_type_list", produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/config/room_type_list", produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> queryRoomType(HttpServletRequest request,@RequestBody(required=true) RoomTypeQueryRequest queryFilter) { BaseApiResponse> processResult = new BaseApiResponse<>(); //-- @@ -62,7 +62,7 @@ public class ConfigController { return ResponseEntity.status(HttpStatus.OK).body(processResult); } - @RequestMapping(value = "/app/config/bed_type_list", produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/config/bed_type_list", produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> queryBedType(HttpServletRequest request,@RequestBody(required=true) BedTypeQueryRequest queryFilter) { BaseApiResponse> processResult = new BaseApiResponse<>(); //-- @@ -72,7 +72,7 @@ public class ConfigController { return ResponseEntity.status(HttpStatus.OK).body(processResult); } - @RequestMapping(value = "/app/config/special_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/config/special_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> queryCfgRoomFeatures(HttpServletRequest request, @RequestBody(required=true) RoomFeatureQueryRequest queryFilter) { BaseApiResponse> processResult = new BaseApiResponse<>(); //-- @@ -82,7 +82,7 @@ public class ConfigController { return ResponseEntity.status(HttpStatus.OK).body(processResult); } - @RequestMapping(value = "/app/config/criteria_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/config/criteria_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> queryRoomCriteria(HttpServletRequest request, @RequestBody(required=true) RoomCriteriaQueryRequest queryParas) throws Exception { BaseApiResponse> processResult = new BaseApiResponse<>(); //-- @@ -92,7 +92,7 @@ public class ConfigController { return ResponseEntity.status(HttpStatus.OK).body(processResult); } - @RequestMapping(value = "/app/config/setting_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/config/setting_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> queryRoomSetting(HttpServletRequest request) { BaseApiResponse> processResult = new BaseApiResponse<>(); //-- @@ -110,7 +110,7 @@ public class ConfigController { return ResponseEntity.status(HttpStatus.OK).body(processResult); } - @RequestMapping(value = "/app/config/available_room_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/config/available_room_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> queryRoomCriteriaAvailableRoom(HttpServletRequest request, @RequestBody(required=true) AvailableRoomQueryRequest queryParas) { BaseApiResponse> processResult = new BaseApiResponse<>(); //-- @@ -121,7 +121,7 @@ public class ConfigController { return ResponseEntity.status(HttpStatus.OK).body(processResult); } - @RequestMapping(value = "/app/config/is_extend_booking", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/config/is_extend_booking", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> queryIsExtendBooking(HttpServletRequest request, @RequestBody(required=true) ExtendBookingQueryRequest queryParas) { BaseApiResponse> processResult = new BaseApiResponse<>(); //-- @@ -132,7 +132,7 @@ public class ConfigController { return ResponseEntity.status(HttpStatus.OK).body(processResult); } - @RequestMapping(value = "/app/config/booking_type_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/config/booking_type_list", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> queryCfgBookingTypes(HttpServletRequest request) { BaseApiResponse> processResult = new BaseApiResponse<>(); List listCfgBookingTypes = configService.listAllBookingType(); @@ -142,7 +142,7 @@ public class ConfigController { return ResponseEntity.status(HttpStatus.OK).body(processResult); } - @RequestMapping(value = "/app/config/get_waitlist_booking", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/config/get_waitlist_booking", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> queryGetWaitlistBooking(HttpServletRequest request, @RequestBody(required=true) WaitlistBookingQueryRequest queryParas) throws Exception { BaseApiResponse> processResult = new BaseApiResponse<>(); //-- diff --git a/src/main/java/com/mgm/mcrm/appsetting/services/ConfigService.java b/src/main/java/com/mgm/mcrm/appsetting/services/ConfigService.java index 37a6c59..9204617 100644 --- a/src/main/java/com/mgm/mcrm/appsetting/services/ConfigService.java +++ b/src/main/java/com/mgm/mcrm/appsetting/services/ConfigService.java @@ -224,14 +224,15 @@ public class ConfigService { if(Strings.CS.equals(property.getCategory(), "OPERA")) isOpera = true; if(Strings.CS.equals(property.getCategory(), "DAYLIGHT")) isDaylight = true; if(room.getForWaitlist() != null && room.getForWaitlist()==1&&room.getForBooking()==0) isWaitlist = true; - - if(isOpera||isWaitlist) { + + if (isOpera) { resultList = getOperaCriteria( blockId,sectionId, bookingTypeId, propertyId, roomId, bedTypeMasterGroupId,userId); } else if (isDaylight) { resultList = getDaylightCriteria( blockId,sectionId, bookingTypeId, propertyId, roomId, bedTypeMasterGroupId,userId); } else { resultList = getOtherCriteria( segmentId, sectionId, bookingTypeId, propertyId, roomId); } + if(!resultList.isEmpty()) { checkCriteriaBooking(resultList, playerId, userId, userName); } @@ -518,7 +519,7 @@ public class ConfigService { List bookingList = bookingViewDao.getCalendarBooking(endDate, playerId); - Set protectionPeriod = new HashSet(); + Set protectionPeriod = new HashSet<>(); for(BookingsView book:bookingList) { if(StringUtils.isEmpty(book.getLastOperaStatus())|| Strings.CS.equals(book.getLastOperaStatus(), "DUE_IN")|| @@ -532,14 +533,14 @@ public class ConfigService { differentUser = !Strings.CS.equals(book.getCreatedByName(), userName); } if(enableProtection&&differentUser) { - if(Integer.valueOf(checkinProtection)>=0) { - for(int q=0;q<=Integer.valueOf(checkinProtection);q++) { - Date protectCheckin = DateTimeUtil.addDay( book.getCheckinDate(), Integer.valueOf(q)); + if(Integer.parseInt(checkinProtection)>=0) { + for(int q = 0; q<=Integer.parseInt(checkinProtection); q++) { + Date protectCheckin = DateTimeUtil.addDay( book.getCheckinDate(), q); protectionPeriod.add(protectCheckin); } }else { - for(int q=0;q>=Integer.valueOf(checkinProtection);q--) { - Date protectCheckin = DateTimeUtil.addDay( book.getCheckinDate(), Integer.valueOf(q)); + for(int q = 0; q>=Integer.parseInt(checkinProtection); q--) { + Date protectCheckin = DateTimeUtil.addDay( book.getCheckinDate(), q); protectionPeriod.add(protectCheckin); } } diff --git a/src/main/java/com/mgm/mcrm/authorization/controllers/MobileLoginController.java b/src/main/java/com/mgm/mcrm/authorization/controllers/MobileLoginController.java index 0fa0266..dbf537f 100644 --- a/src/main/java/com/mgm/mcrm/authorization/controllers/MobileLoginController.java +++ b/src/main/java/com/mgm/mcrm/authorization/controllers/MobileLoginController.java @@ -32,6 +32,7 @@ import jakarta.validation.Valid; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.util.Base64Util; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -41,7 +42,6 @@ import java.util.List; @Tag(name = "Authorization", description = "APIs for Authorization functions") @RestController -@CrossOrigin(origins = "*") @RequestMapping("/api") public class MobileLoginController { @Resource @@ -60,7 +60,7 @@ public class MobileLoginController { private SysGroupPermissionService sysGroupPermissionService; // 该方法用于模拟sso登录,生产环境下不适用 - @RequestMapping(value = "/app/sso/mock_user_sso_login", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/sso/mock_user_sso_login", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity mockupUserLogin(HttpServletRequest request, HttpServletResponse response, @RequestBody(required=true) @Valid AccountLoginRequest accountLoginRequest) throws IOException { AccountLoginResponse result = new AccountLoginResponse(); String userId = accountLoginRequest.getUserId(); @@ -109,7 +109,7 @@ public class MobileLoginController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/app/sso/user_sso_login", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/sso/user_sso_login", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity userSsoLogin(HttpServletRequest request, HttpServletResponse response, @RequestBody(required=true) SsoLoginRequest ssoLoginRequest) throws IOException { SsoLoginResponse result = new SsoLoginResponse(); LoginUserInfo loginUser= null; @@ -191,7 +191,7 @@ public class MobileLoginController { return ResponseEntity.status(HttpStatus.OK).body(result); } // 接入SSO的时候修改此方法 - @RequestMapping(value = "/app/sso/get_sso_token_by_code", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/sso/get_sso_token_by_code", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity getTokenByCode(HttpServletRequest request, HttpServletResponse response, @RequestBody(required=true) SsoLoginRequest ssoLoginRequest) { String code = ssoLoginRequest.getCode(); boolean mockSSO = ssoLoginRequest.isMockSSO(); @@ -244,14 +244,14 @@ public class MobileLoginController { } return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/app/me", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/me", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public HashMap verifyToken(HttpServletRequest request, HttpServletResponse response) { HashMap objReturn = new HashMap(); // 这里可以return一些信息,每次刷新页面或者跳转页面的时候重新拿一次某些信息。 objReturn.put("status", 200); return objReturn; } - @RequestMapping(value = "/app/refresh_token", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/refresh_token", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity refreshToken(HttpServletRequest request, HttpServletResponse response) throws IOException { String isReNew = request.getParameter("isRenewRefreshToken"); @@ -276,7 +276,7 @@ public class MobileLoginController { return result; } - @RequestMapping(value = "/web/user/lookup", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/user/lookup", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> lookupUser(HttpServletRequest request, HttpServletResponse response, @RequestBody UserLookupRequest params) throws Exception { BaseApiResponse result = new BaseApiResponse<>(); @@ -304,7 +304,7 @@ public class MobileLoginController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/web/user/lookup_name", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/user/lookup_name", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> lookupUserName(HttpServletRequest request, HttpServletResponse response, @RequestBody UserLookupRequest params) throws Exception { String userName = params.getUserName(); @@ -316,7 +316,7 @@ public class MobileLoginController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/web/user/check_approver", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/user/check_approver", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> checkApprover(HttpServletRequest request, HttpServletResponse response, @RequestBody CheckApproverResult params) throws Exception { BaseApiResponse result = new BaseApiResponse<>(); @@ -329,7 +329,7 @@ public class MobileLoginController { userId = userId.replace("@mgm.mo", ""); } - if (StringUtils.isAnyEmpty(userId, userName, roomType, property)) { + if (StringUtils.isAnyEmpty(userId, roomType, property)) { result.setSuccess(false); result.setMessage("Miss parameter"); return ResponseEntity.status(HttpStatus.OK).body(result); diff --git a/src/main/java/com/mgm/mcrm/authorization/services/MgmUserService.java b/src/main/java/com/mgm/mcrm/authorization/services/MgmUserService.java index 67c0396..1ecd722 100644 --- a/src/main/java/com/mgm/mcrm/authorization/services/MgmUserService.java +++ b/src/main/java/com/mgm/mcrm/authorization/services/MgmUserService.java @@ -25,7 +25,7 @@ import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; import com.mgm.mcrm.common.dtos.mgm_api.CheckApproverResult; import com.mgm.mcrm.common.dtos.mgm_api.UserInfoApiRecord; import com.mgm.mcrm.common.dtos.mgm_api.UserInfoApiResult; -import com.mgm.mcrm.common.services.MGMApiService; +import com.mgm.mcrm.common.services.MGMEsbApiService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @@ -40,7 +40,7 @@ public class MgmUserService { private boolean calendarPermissionMockEnabled; @Resource - private MGMApiService mgmApiService; + private MGMEsbApiService mgmEsbApiService; @Resource private PropertyDao propertyDao; @Resource @@ -61,7 +61,7 @@ public class MgmUserService { public BaseApiResponse findUserInfoByUserId(String userId) { BaseApiResponse result = new BaseApiResponse<>(); try { - HttpResponseResult httpResult = mgmApiService.invokeGetUserInfo(userId,""); + HttpResponseResult httpResult = mgmEsbApiService.invokeGetUserInfo(userId,""); if(httpResult.getStatusCode()==200) { try { UserInfoApiResult resultObj = JSONObject.parseObject(httpResult.getResponseContent(),UserInfoApiResult.class,Feature.SupportNonPublicField,Feature.IgnoreNotMatch); @@ -115,7 +115,7 @@ public class MgmUserService { BaseApiResponse> result = new BaseApiResponse<>(); String userId = SysCommon.mgmApiMockup?"80001":""; try { - HttpResponseResult httpResult = mgmApiService.invokeGetUserInfo(userId,userName); + HttpResponseResult httpResult = mgmEsbApiService.invokeGetUserInfo(userId,userName); if(httpResult.getStatusCode()==200) { try { UserInfoApiResult resultObj = JSONObject.parseObject(httpResult.getResponseContent(),UserInfoApiResult.class,Feature.SupportNonPublicField,Feature.IgnoreNotMatch); @@ -161,7 +161,7 @@ public class MgmUserService { public BaseApiResponse checkApprover(String userId,String userName,String roomType,String property) { BaseApiResponse result = new BaseApiResponse<>(); try { - HttpResponseResult httpResult = mgmApiService.invokeCheckApprover(userId,userName,roomType,property); + HttpResponseResult httpResult = mgmEsbApiService.invokeCheckApprover(userId,userName,roomType,property); if(httpResult.getStatusCode()==200) { try { CheckApproverResult resultObj = JSONObject.parseObject(httpResult.getResponseContent(),CheckApproverResult.class,Feature.SupportNonPublicField,Feature.IgnoreNotMatch); diff --git a/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgMarketCodes.java b/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgMarketCodes.java index 552063e..0a00da1 100644 --- a/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgMarketCodes.java +++ b/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgMarketCodes.java @@ -30,4 +30,7 @@ public class CfgMarketCodes implements Serializable { @Column(name = "category") private String category; + + @Column(name = "property_id") + private Integer propertyId; } diff --git a/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgRateCodes.java b/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgRateCodes.java index 624efb2..88c24bd 100644 --- a/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgRateCodes.java +++ b/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgRateCodes.java @@ -29,4 +29,7 @@ public class CfgRateCodes implements Serializable { private String dlRatePlanId; @Column(name = "category") private String category; + + @Column(name = "property_id") + private Integer propertyId; } diff --git a/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgSourceCodes.java b/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgSourceCodes.java index 9d782e7..4fa97df 100644 --- a/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgSourceCodes.java +++ b/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgSourceCodes.java @@ -14,7 +14,7 @@ public class CfgSourceCodes implements Serializable { @Id @Column(name = "id") @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + private Long id; @Column(name="code") private String code; @Column(name="created_at") @@ -27,4 +27,7 @@ public class CfgSourceCodes implements Serializable { private String updatedBy; @Column(name="dl_source_id") private String dlSourceId; + + @Column(name = "property_id") + private Integer propertyId; } diff --git a/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgTiers.java b/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgTiers.java index 79bfdb8..a3dc71a 100644 --- a/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgTiers.java +++ b/src/main/java/com/mgm/mcrm/basesetting/daylight/models/CfgTiers.java @@ -31,4 +31,7 @@ public class CfgTiers implements Serializable { private Date updatedAt; @Column(name="updated_by") private String updatedBy; + + @Column(name = "property_id") + private Integer propertyId; } diff --git a/src/main/java/com/mgm/mcrm/basesetting/daylight/repositories/RateCodeDao.java b/src/main/java/com/mgm/mcrm/basesetting/daylight/repositories/RateCodeDao.java index 1b276ea..23a6efb 100644 --- a/src/main/java/com/mgm/mcrm/basesetting/daylight/repositories/RateCodeDao.java +++ b/src/main/java/com/mgm/mcrm/basesetting/daylight/repositories/RateCodeDao.java @@ -24,9 +24,6 @@ public interface RateCodeDao extends JpaRepository { @Modifying @Query(value = " delete from cfg_rate_codes where id=?1 ",nativeQuery=true) int deleteById(long id); - - @Query(value="select * from cfg_rate_codes where id =?1",nativeQuery=true) - CfgRateCodes getById(long id); List findByCodeAndCategory(String ratePlanCode, String category); diff --git a/src/main/java/com/mgm/mcrm/basesetting/daylight/repositories/SourceCodeDao.java b/src/main/java/com/mgm/mcrm/basesetting/daylight/repositories/SourceCodeDao.java index 3801f98..d27ba78 100644 --- a/src/main/java/com/mgm/mcrm/basesetting/daylight/repositories/SourceCodeDao.java +++ b/src/main/java/com/mgm/mcrm/basesetting/daylight/repositories/SourceCodeDao.java @@ -16,6 +16,7 @@ import java.util.List; public interface SourceCodeDao extends JpaRepository { CfgSourceCodes findById(long id); + @Query(value="select top(1) * from cfg_source_codes with (nolock) where dl_source_id=?1", nativeQuery=true) CfgSourceCodes findByDlSourceId(String dlSourceId); List findAll(Specification spec,Sort sort); List findAll(Specification spec); diff --git a/src/main/java/com/mgm/mcrm/basesetting/daylight/repositories/TierDao.java b/src/main/java/com/mgm/mcrm/basesetting/daylight/repositories/TierDao.java index 1b61b0c..db941b5 100644 --- a/src/main/java/com/mgm/mcrm/basesetting/daylight/repositories/TierDao.java +++ b/src/main/java/com/mgm/mcrm/basesetting/daylight/repositories/TierDao.java @@ -10,10 +10,12 @@ import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; public interface TierDao extends JpaRepository { - CfgTiers findByTierNameAndCategory(String string, String category); + CfgTiers findByTierNameAndCategoryAndPropertyId(String string, String category, Long propertyId); List findAll(Specification spec); List findAll(Specification spec, Sort sort); Page findAll(Specification spec, Pageable pageable); CfgTiers findByDaylightTier(String daylightTier); + + CfgTiers findByTierNameAndCategory(String string, String category); } diff --git a/src/main/java/com/mgm/mcrm/basesetting/daylight/services/CompRuleService.java.bak b/src/main/java/com/mgm/mcrm/basesetting/daylight/services/CompRuleService.java.bak new file mode 100644 index 0000000..5415cd6 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/basesetting/daylight/services/CompRuleService.java.bak @@ -0,0 +1,351 @@ +package com.mgm.mcrm.basesetting.daylight.services; + +import com.mgm.mcrm.basesetting.daylight.dtos.request.*; +import com.mgm.mcrm.basesetting.daylight.dtos.response.CompRuleResponse; +import com.mgm.mcrm.basesetting.daylight.dtos.response.RequestorApproverResponse; +import com.mgm.mcrm.basesetting.daylight.models.CfgCompPreset; +import com.mgm.mcrm.basesetting.daylight.models.CfgDaylightView; +import com.mgm.mcrm.basesetting.daylight.repositories.CompPresetDao; +import com.mgm.mcrm.common.dtos.BaseApiResponse; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.jspecify.annotations.NonNull; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +@Slf4j +@Service +public class CompRuleService { + + @Resource + private DaylightApiService daylightApiService; + + @Resource + private CompPresetDao compPresetDao; + + public BaseApiResponse createCompRule( + String dlPropertyId, + String dlAccountId, + CfgDaylightView roomOnly, + CfgDaylightView miscChargeOrExtend, + String startDate, + String endDate, + String miscCharge, + Integer category) { + + if (category == 1) { + return createRoomOnlyCompRule(dlPropertyId, dlAccountId, roomOnly); + } + + if (category == 2) { + return createRoomCompRule(dlPropertyId, dlAccountId, miscChargeOrExtend, miscCharge, category); + } + + if (category == 3) { + return createExtendRoomOnlyCompRule(dlPropertyId, dlAccountId, roomOnly, startDate, endDate); + } + + if (category == 4) { + return createExtendRoomCompRule(); + } + + return null; + } + + private BaseApiResponse createRoomOnlyCompRule( + String dlPropertyId, + String dlAccountId, + CfgDaylightView roomOnly + ) { + + + + return null; + } + + private BaseApiResponse createRoomCompRule( + String dlPropertyId, + String dlAccountId, + CfgDaylightView miscChargeOrExtend, + String miscCharge, + Integer category + ) { + + return null; + } + + private BaseApiResponse createExtendRoomOnlyCompRule( + String dlPropertyId, + String dlAccountId, + CfgDaylightView roomOnly, + String startDate, + String endDate) { + + + return null; + } + + private BaseApiResponse createExtendRoomCompRule( + + ) { + return null; + } + + public BaseApiResponse updateCompRule( + String dlPropertyId, + String dlAccountId, + String dlCompRuleId, + String startDate, + String endDate, + CfgDaylightView miscChargeOrExtend, + String miscCharge, + Integer category) { + + if (category == 2) { + return updateRoomCompRule(dlPropertyId, dlAccountId, dlCompRuleId, startDate, endDate, miscChargeOrExtend, miscCharge); + } + + if (category == 3) { + return updateExtendRoomOnlyCompRule(dlPropertyId, dlAccountId, dlCompRuleId, startDate, endDate, miscChargeOrExtend, miscCharge); + } + + if (category == 4) { + + } + + BaseApiResponse response = new BaseApiResponse<>(); + + response.setSuccess(false); + response.setMessage("Invalid category"); + + return response; + } + + private BaseApiResponse updateRoomCompRule( + String dlPropertyId, + String dlAccountId, + String dlCompRuleId, + String startDate, + String endDate, + CfgDaylightView miscChargeOrExtend, + String miscCharge + ) { + UpdateCompRuleRequest updateCompRuleRequest = new UpdateCompRuleRequest(); + + BaseApiResponse compRuleResponse = daylightApiService.getDaylightCompRule(dlPropertyId, dlAccountId, dlCompRuleId); + + if (!compRuleResponse.isSuccess() || compRuleResponse.getData() == null) { + BaseApiResponse result = new BaseApiResponse<>(); + result.setSuccess(false); + result.setMessage(compRuleResponse.getMessage()); + return result; + } + + CompRuleResponse crr = compRuleResponse.getData(); + + ComplimentaryRuleLimits crl = + getMiscChargeOrExtendCompRuleLimits(startDate, endDate, miscCharge, miscChargeOrExtend.getSoftLimit(), 2); + + updateCompRuleRequest.setComplimentaryRuleLimits(crl); + + return daylightApiService.updateDaylightCompRule(dlPropertyId, dlAccountId, dlCompRuleId, String.valueOf(crr.getVersion()), updateCompRuleRequest); + } + + private BaseApiResponse updateExtendRoomOnlyCompRule( + String dlPropertyId, + String dlAccountId, + String dlCompRuleId, + String startDate, + String endDate, + CfgDaylightView miscChargeOrExtend, + String miscCharge + ) { + UpdateCompRuleRequest updateCompRuleRequest = new UpdateCompRuleRequest(); + + BaseApiResponse compRuleResponse = daylightApiService.getDaylightCompRule(dlPropertyId, dlAccountId, dlCompRuleId); + + if (!compRuleResponse.isSuccess() || compRuleResponse.getData() == null) { + BaseApiResponse result = new BaseApiResponse<>(); + result.setSuccess(false); + result.setMessage(compRuleResponse.getMessage()); + return result; + } + + CompRuleResponse crr = compRuleResponse.getData(); + + ComplimentaryRuleLimits crl = + getMiscChargeOrExtendCompRuleLimits(startDate, endDate, miscCharge, miscChargeOrExtend.getSoftLimit(), 3); + + updateCompRuleRequest.setComplimentaryRuleLimits(crl); + + return daylightApiService.updateDaylightCompRule(dlPropertyId, dlAccountId, dlCompRuleId, String.valueOf(crr.getVersion()), updateCompRuleRequest); + } + + // 使用BigDecimal来计算金额以保留精度 by Mauve 2026-07-22 + private static BigDecimal calculateAmount(String amountStr, String percentStr) { + + BigDecimal amount = new BigDecimal(amountStr); + + BigDecimal percent = new BigDecimal(percentStr) + .divide(BigDecimal.valueOf(100), 4, RoundingMode.HALF_UP); + + return amount.multiply(percent) + .setScale(2, RoundingMode.HALF_UP); + } + + private static @NonNull ComplimentaryRuleLimits getMiscChargeOrExtendCompRuleLimits( + String startDate, + String endDate, + String miscCharge, + String daylightPercentage, + Integer category + ) { + AmountLimit totalAmountLimit = new AmountLimit(); + totalAmountLimit.setCurrency("MOP"); + totalAmountLimit.setAmount(new BigDecimal(miscCharge.trim())); + + AmountLimit softTotalAmountLimit = new AmountLimit(); + softTotalAmountLimit.setCurrency("MOP"); + softTotalAmountLimit.setAmount(calculateAmount(miscCharge.trim(), daylightPercentage)); + + ComplimentaryRuleLimits complimentaryRuleLimits = new ComplimentaryRuleLimits(); + + if (category == 2) { + complimentaryRuleLimits.setTotalAmountLimit(totalAmountLimit); + complimentaryRuleLimits.setSoftTotalAmountLimit(softTotalAmountLimit); + } + + if (category == 3) { + CompRuleDateRange dateRange = new CompRuleDateRange(); + dateRange.setStart(startDate); + dateRange.setEnd(endDate); + complimentaryRuleLimits.setDateRanges(List.of(dateRange)); + } + + return complimentaryRuleLimits; + } + + /** + * TODO 暂时写死, 之后参数可能需要多加几个 by Mauve + * @param startDate + * @param endDate + * @return + */ + private static @NonNull ComplimentaryRuleLimits getComplimentaryRuleLimits(String startDate, String endDate) { + + CompRuleDateRange dateRange = new CompRuleDateRange(); + dateRange.setStart(startDate); + dateRange.setEnd(endDate); + + ComplimentaryRuleLimits complimentaryRuleLimits = new ComplimentaryRuleLimits(); + complimentaryRuleLimits.setDateRanges(List.of(dateRange)); + + return complimentaryRuleLimits; + } + + private CreateCompRuleRequest buildRoomOnlyCompRuleRequest( + String profileId, + String startDate, + String endDate, + RequestorApproverResponse requestorApprover, + CfgDaylightView daylightView, + boolean setRuleLimits) { + CreateCompRuleRequest crr = new CreateCompRuleRequest(); + + crr.setComplimentaryTypeId(daylightView.getDlCompTypeId()); + crr.setPaymentMethodTransactionCodeId(daylightView.getDlPaymentMethodCodeId()); + crr.setExecutive(false); + crr.setDoNotIssueInvoice(false); + crr.setProfileId(profileId); + if(setRuleLimits) crr.setComplimentaryRuleLimits(getComplimentaryRuleLimits(startDate, endDate)); + + List compPresetList = compPresetDao.findAllByDaylightMappingId(daylightView.getId()); + CfgCompPreset compPreset = compPresetList.getFirst(); + + crr.setComplimentaryAccountingPresetsIds(List.of(compPreset.getDlCompPresetId())); + crr.setJustification(""); + crr.setRequestorId(requestorApprover.getId()); + crr.setApproverId(requestorApprover.getId()); + + return crr; + } + + private BaseApiResponse buildMiscChargeOrExtendCompRuleRequest( + String profileId, + String startDate, + String endDate, + CfgDaylightView daylightView, + RequestorApproverResponse requestorApprover, + String miscCharge, + Integer category) { + BaseApiResponse result = new BaseApiResponse<>(); + + if (daylightView == null) { + result.setSuccess(false); + result.setMessage("Daylight config is empty"); + log.error("Daylight config is empty"); + return result; + } + + if (StringUtils.isBlank(daylightView.getDlCompTypeId())) { + result.setSuccess(false); + result.setMessage("Daylight comp type id is empty"); + log.error("Daylight comp type id is empty"); + return result; + } + + if (StringUtils.isBlank(daylightView.getDlPaymentMethodCodeId())) { + result.setSuccess(false); + result.setMessage("Daylight payment method code id is empty"); + log.error("Daylight payment method code id is empty"); + return result; + } + + List capIds = new ArrayList<>(); + if (CollectionUtils.isEmpty(daylightView.getCfgCompPresetList())) { + result.setSuccess(false); + result.setMessage("Daylight comp preset id is empty"); + log.error("Daylight comp preset id is empty"); + return result; + } + + for (CfgCompPreset cfgCompPreset : daylightView.getCfgCompPresetList()) { + if (cfgCompPreset != null && StringUtils.isNotBlank(cfgCompPreset.getDlCompPresetId())) { + capIds.add(cfgCompPreset.getDlCompPresetId()); + } + } + + List distinctCapIds = new ArrayList<>(new HashSet<>(capIds)); + + if (CollectionUtils.isEmpty(capIds)) { + result.setSuccess(false); + result.setMessage("Daylight comp preset id is empty"); + log.error("Daylight comp preset id is empty"); + return result; + } + + CreateCompRuleRequest crr = new CreateCompRuleRequest(); + crr.setComplimentaryTypeId(daylightView.getDlCompTypeId()); + crr.setPaymentMethodTransactionCodeId(daylightView.getDlPaymentMethodCodeId()); + crr.setExecutive(false); + crr.setDoNotIssueInvoice(false); + crr.setProfileId(profileId); + crr.setComplimentaryRuleLimits(getMiscChargeOrExtendCompRuleLimits(startDate, endDate, miscCharge, daylightView.getSoftLimit(), category)); + crr.setComplimentaryAccountingPresetsIds(distinctCapIds); + crr.setJustification(""); + crr.setRequestorId(requestorApprover.getId()); + crr.setApproverId(requestorApprover.getId()); + + result.setSuccess(true); + result.setData(crr); + + return result; + } +} diff --git a/src/main/java/com/mgm/mcrm/basesetting/daylight/services/DaylightApiService.java b/src/main/java/com/mgm/mcrm/basesetting/daylight/services/DaylightApiService.java index a99f6a3..410e734 100644 --- a/src/main/java/com/mgm/mcrm/basesetting/daylight/services/DaylightApiService.java +++ b/src/main/java/com/mgm/mcrm/basesetting/daylight/services/DaylightApiService.java @@ -3,6 +3,7 @@ package com.mgm.mcrm.basesetting.daylight.services; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; +import com.mgm.mcrm.authorization.services.MgmUserService; import com.mgm.mcrm.basesetting.agebucket.models.SysAgebucket; import com.mgm.mcrm.basesetting.agebucket.services.AgebucketService; import com.mgm.mcrm.basesetting.daylight.dtos.request.*; @@ -28,7 +29,9 @@ import com.mgm.mcrm.booking.repositories.BookingDao; import com.mgm.mcrm.booking.services.BookingService; import com.mgm.mcrm.common.dtos.BaseApiResponse; import com.mgm.mcrm.common.dtos.HttpResult; +import com.mgm.mcrm.common.dtos.config.ApiGatewaySetting; import com.mgm.mcrm.common.dtos.mgm_api.PlayerProfileEntry; +import com.mgm.mcrm.common.dtos.mgm_api.UserInfoApiRecord; import com.mgm.mcrm.common.enums.EnumBookingStatus; import com.mgm.mcrm.common.httpclient.HttpHeaderBuilder; import com.mgm.mcrm.common.services.HttpService; @@ -43,7 +46,6 @@ import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.hc.core5.http.Header; import org.jspecify.annotations.NonNull; -import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.math.BigDecimal; @@ -88,8 +90,11 @@ public class DaylightApiService { @Resource private CompPresetDao compPresetDao; - @Value("${daylight.system-id}") - private String dlSystemId; + @Resource + private ApiGatewaySetting apiGatewaySetting; + + @Resource + private MgmUserService mgmUserService; public BaseApiResponse daylightUpsertReservationNew(ReservationRequestDTO requestDTO, String dlPropertyId) { BaseApiResponse result = new BaseApiResponse<>(); @@ -943,7 +948,7 @@ public class DaylightApiService { cfgProperties.getDlPropertyId(), properties.size(), profileId); try { - String url = "/api-gateway/profiles/v1/individual/"+profileId; + String url = "/api-gateway/profiles/v1/individual/" + profileId + "/up-to-date"; // 获取对应的值 Header[] headers = HttpHeaderBuilder.custom() .contentType("application/json") @@ -1083,7 +1088,7 @@ public class DaylightApiService { JSONObject res = JSON.parseObject(responseData); JSONArray bookings = res.getJSONArray("results"); log.info("[getBookingRecordByProfileId_Optimize] Property dlPropertyId={}, bookings array size={}", dlPropertyId, bookings != null ? bookings.size() : 0); - bookingList.addAll(convertBookingDtoList(bookings, dlPropertyId, playerId)); + bookingList.addAll(convertBookingDtoList(bookings, dlPropertyId, playerId, property.getId())); } } catch (Exception e) { log.error("[getBookingRecordByProfileId_Optimize] Exception while querying Daylight bookings, profileId={}, error={}", profileId, e.getMessage(), e); @@ -1093,7 +1098,7 @@ public class DaylightApiService { return bookingList; } - public List convertBookingDtoList(JSONArray bookings, String dlPropertyId, String playerId){ + public List convertBookingDtoList(JSONArray bookings, String dlPropertyId, String playerId, Long propertyId) { List bookingList = new ArrayList<>(); if(bookings==null || bookings.isEmpty()) { log.info("[convertBookingDtoList] bookings is null or empty for dlPropertyId={}", dlPropertyId); @@ -1155,6 +1160,7 @@ public class DaylightApiService { booking.setDlGuaranteeTypeId(bookingRes.getString("guaranteeTypeId")); booking.setDlProfileId(bookingRes.getString("profileId")); booking.setDlBlockId(bookingRes.getString("blockId")); + booking.setPropertyId(propertyId); JSONObject sc = bookingRes.getJSONObject("statusCode"); @@ -1178,6 +1184,12 @@ public class DaylightApiService { String booker = valueJson.getString("Booker"); String approver = valueJson.getString("Approver"); + BaseApiResponse userInfoRes = mgmUserService.findUserInfoByUserId(booker); + if(userInfoRes.isSuccess()) { + UserInfoApiRecord userInfo = userInfoRes.getData(); + booking.setSectionId(userInfo.getSectionId()); + } + if (StringUtils.isNotBlank(booker)) { booking.setCreatedBy(booker); booking.setBooker(booker); @@ -1259,7 +1271,7 @@ public class DaylightApiService { String lastName = profileObj.getDetails().getLastName(); if (lastName != null && firstName != null) { - sharerName = lastName + firstName; + sharerName = lastName + " " + firstName; } else if (lastName != null) { sharerName = lastName; } else if (firstName != null) { @@ -1466,6 +1478,7 @@ public class DaylightApiService { String profileId, String dlPropertyId, String userId, + String roomName, String startDate, String endDate, List daylightViewList, @@ -1541,27 +1554,33 @@ public class DaylightApiService { .build(); String approver = daylightViewRoomOnly.getApprover(); - - BaseApiResponse requestorApproverByUserId; - if (StringUtils.isNotEmpty(approver)) { - requestorApproverByUserId = getDaylightRequestorApproverByUserId(dlPropertyId, approver); - } else { - requestorApproverByUserId = getDaylightRequestorApproverByUserId(dlPropertyId, userId); - } + boolean isMansion = StringUtils.containsIgnoreCase(roomName, "mansion"); + String requestorUserId = StringUtils.isNotEmpty(approver) && isMansion ? userId + : StringUtils.isNotEmpty(approver) ? approver : userId; + BaseApiResponse requestorResponse = + getDaylightRequestorApproverByUserId(dlPropertyId, requestorUserId); + BaseApiResponse approverResponse = + StringUtils.isNotEmpty(approver) && !isMansion + ? requestorResponse + : StringUtils.isNotEmpty(approver) + ? getDaylightRequestorApproverByUserId(dlPropertyId, approver) + : requestorResponse; // 如果查不到 requestor/approver 直接返回 false - if (!requestorApproverByUserId.isSuccess()) { + if (!requestorResponse.isSuccess() || !approverResponse.isSuccess()) { result.setSuccess(false); - result.setMessage(requestorApproverByUserId.getMessage()); + result.setMessage(!requestorResponse.isSuccess() + ? requestorResponse.getMessage() : approverResponse.getMessage()); return result; } - RequestorApproverResponse returnData = requestorApproverByUserId.getData(); + RequestorApproverResponse requestor = requestorResponse.getData(); + RequestorApproverResponse approverData = approverResponse.getData(); if (createRoomOnlyCompRule && daylightViewRoomOnly != null) { CreateCompRuleRequest roomOnlyCompRuleRequest = buildRoomOnlyCompRuleRequest( - profileId, startDate, endDate, returnData, daylightViewRoomOnly,false); + profileId, startDate, endDate, requestor, approverData, daylightViewRoomOnly,false); BaseApiResponse createDefaultResult = createCompRule(url, headers, roomOnlyCompRuleRequest); if (!createDefaultResult.isSuccess()) { return createDefaultResult; @@ -1570,7 +1589,7 @@ public class DaylightApiService { if (createDaylightViewCompRule) { BaseApiResponse daylightViewCompRuleRequest = buildDaylightViewCompRuleRequest( - profileId, startDate, endDate, daylightView, returnData, miscCharge); + profileId, startDate, endDate, daylightView, requestor, approverData, miscCharge); if (!daylightViewCompRuleRequest.isSuccess()) { result.setSuccess(false); result.setMessage(daylightViewCompRuleRequest.getMessage()); @@ -1647,7 +1666,7 @@ public class DaylightApiService { RequestorApproverResponse returnData = requestorApproverByUserId.getData(); CreateCompRuleRequest roomOnlyCompRuleRequest = buildRoomOnlyCompRuleRequest( - profileId, startDate, endDate, returnData, daylightViewRoomOnly,true); + profileId, startDate, endDate, returnData, returnData, daylightViewRoomOnly,true); BaseApiResponse createDefaultResult = createCompRule(url, headers, roomOnlyCompRuleRequest); if (!createDefaultResult.isSuccess()) { return createDefaultResult; @@ -1671,7 +1690,8 @@ public class DaylightApiService { String profileId, String startDate, String endDate, - RequestorApproverResponse requestorApprover, + RequestorApproverResponse requestor, + RequestorApproverResponse approver, CfgDaylightView daylightView, boolean setRuleLimits) { CreateCompRuleRequest crr = new CreateCompRuleRequest(); @@ -1688,8 +1708,8 @@ public class DaylightApiService { crr.setComplimentaryAccountingPresetsIds(List.of(compPreset.getDlCompPresetId())); crr.setJustification(""); - crr.setRequestorId(requestorApprover.getId()); - crr.setApproverId(requestorApprover.getId()); + crr.setRequestorId(requestor.getId()); + crr.setApproverId(approver.getId()); return crr; } @@ -1699,7 +1719,8 @@ public class DaylightApiService { String startDate, String endDate, CfgDaylightView daylightView, - RequestorApproverResponse requestorApprover, + RequestorApproverResponse requestor, + RequestorApproverResponse approver, String miscCharge) { BaseApiResponse result = new BaseApiResponse<>(); @@ -1756,8 +1777,8 @@ public class DaylightApiService { crr.setComplimentaryRuleLimits(getMiscChargeComplimentaryRuleLimits(startDate, endDate, miscCharge, daylightView.getSoftLimit())); crr.setComplimentaryAccountingPresetsIds(distinctCapIds); crr.setJustification(""); - crr.setRequestorId(requestorApprover.getId()); - crr.setApproverId(requestorApprover.getId()); + crr.setRequestorId(requestor.getId()); + crr.setApproverId(approver.getId()); result.setSuccess(true); result.setData(crr); @@ -2291,7 +2312,7 @@ public class DaylightApiService { ReservationRequestDTO.ExternalIdDTO externalIdDTO = new ReservationRequestDTO.ExternalIdDTO(); - externalIdDTO.setSystemId(dlSystemId); + externalIdDTO.setSystemId(apiGatewaySetting.getDaylightSystemId()); externalIdDTO.setNumber(dlExternalNo); map.put("externalIds", List.of(externalIdDTO)); diff --git a/src/main/java/com/mgm/mcrm/basesetting/property/repositories/PropertyDao.java b/src/main/java/com/mgm/mcrm/basesetting/property/repositories/PropertyDao.java index dffd981..e435a12 100644 --- a/src/main/java/com/mgm/mcrm/basesetting/property/repositories/PropertyDao.java +++ b/src/main/java/com/mgm/mcrm/basesetting/property/repositories/PropertyDao.java @@ -27,6 +27,9 @@ public interface PropertyDao extends JpaRepository { @Query(value="select * from cfg_properties with (nolock) where hotel_code=?1",nativeQuery=true) CfgProperties findByHotelCode(String hotelCode); + @Query(value="select top(1) * from cfg_properties with (nolock) where hotel_code=?1 and category=?2", nativeQuery=true) + CfgProperties findByHotelCodeAndCategory(String hotelCode, String category); + @Query(value="select * from cfg_properties with (nolock) where dl_property_id=?1",nativeQuery=true) CfgProperties getByDlPropertyId(String propertyId); diff --git a/src/main/java/com/mgm/mcrm/basesetting/room/models/CfgRoomTypes.java b/src/main/java/com/mgm/mcrm/basesetting/room/models/CfgRoomTypes.java index 8fa6bed..16c6da8 100644 --- a/src/main/java/com/mgm/mcrm/basesetting/room/models/CfgRoomTypes.java +++ b/src/main/java/com/mgm/mcrm/basesetting/room/models/CfgRoomTypes.java @@ -31,4 +31,7 @@ public class CfgRoomTypes implements Serializable { private String dlRoomTypeId; @Column(name = "category") private String category; + + @Column(name = "property_id") + private Integer propertyId; } diff --git a/src/main/java/com/mgm/mcrm/basesetting/room/repositories/RoomTypeDao.java b/src/main/java/com/mgm/mcrm/basesetting/room/repositories/RoomTypeDao.java index 291439f..21476ad 100644 --- a/src/main/java/com/mgm/mcrm/basesetting/room/repositories/RoomTypeDao.java +++ b/src/main/java/com/mgm/mcrm/basesetting/room/repositories/RoomTypeDao.java @@ -28,5 +28,5 @@ public interface RoomTypeDao extends JpaRepository { @Query(value="select * from cfg_room_types where id =?1",nativeQuery=true) CfgRoomTypes getById(Long id); - CfgRoomTypes findByDlRoomTypeId(String dlRoomTypeId); + CfgRoomTypes findByDlRoomTypeIdAndPropertyId(String dlRoomTypeId, Long propertyId); } diff --git a/src/main/java/com/mgm/mcrm/booking/controllers/BookingController.java b/src/main/java/com/mgm/mcrm/booking/controllers/BookingController.java index 3824d49..7566bf7 100644 --- a/src/main/java/com/mgm/mcrm/booking/controllers/BookingController.java +++ b/src/main/java/com/mgm/mcrm/booking/controllers/BookingController.java @@ -58,7 +58,6 @@ import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @RestController -@CrossOrigin(origins = "*") @RequestMapping("/api") public class BookingController { @@ -73,8 +72,6 @@ public class BookingController { @Resource private SysSettingService sysSettingService; @Resource - private OperaService operaSerivce; - @Resource private PlayerService playerService; @Resource private MgmUserService mgmUserService; @@ -92,7 +89,7 @@ public class BookingController { private SysGroupPermissionService sysGroupPermissionService; - @RequestMapping(value = "/app/booking/get_bookings_by_nameId", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/booking/get_bookings_by_nameId", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getBookingsByNameId(HttpServletRequest request, @RequestBody GetBookingByNameIdRequest params) { Integer startIndex = params.getStartIndex(); @@ -157,7 +154,7 @@ public class BookingController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = {"/app/booking/get_opera_booking_by_conf", "/app/booking/get_booking_by_conf"}, method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = {"/app/booking/get_opera_booking_by_conf", "/app/booking/get_booking_by_conf"}, method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> getBookingByConfirmationNo(HttpServletRequest request, @RequestBody GetOperaBookingRequest params) throws Exception { LoginUserInfo loginUser = SysCommon.parseUserFromToken(request); @@ -204,7 +201,7 @@ public class BookingController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/app/booking/get_booking_by_id", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/booking/get_booking_by_id", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> getBookingById( HttpServletRequest request, @RequestBody GetBookingByIdRequest params) { @@ -252,7 +249,7 @@ public class BookingController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/app/booking/get_bookings_by_groupId", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/booking/get_bookings_by_groupId", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> getBookingsByGroupId( HttpServletRequest request, @RequestBody GetBookingByGroupRequest params) { @@ -265,7 +262,7 @@ public class BookingController { } @OperationLog(actionName="Create Booking", eventCode = "booking_create") - @RequestMapping(value = "/app/booking/create", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/booking/create", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> createBooking(HttpServletRequest request, @RequestBody BookingDto booking) throws Exception { LoginUserInfo loginUser = SysCommon.parseUserFromToken(request); String userId = loginUser.getLoginId(); @@ -315,7 +312,7 @@ public class BookingController { } @OperationLog(actionName="Edit Booking", eventCode = "booking_edit") - @RequestMapping(value = "/app/booking/edit", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/booking/edit", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> editBooking(HttpServletRequest request, @RequestBody BookingDto booking) throws Exception { LoginUserInfo loginUser = SysCommon.parseUserFromToken(request); String userId = loginUser.getLoginId(); @@ -366,7 +363,7 @@ public class BookingController { } @OperationLog(actionName="Cancel Waitlist Booking", eventCode = "waitlist_booking_cancel") - @RequestMapping(value = "/app/booking/cancel_waitlist", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/booking/cancel_waitlist", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> cancelWaitlistBooking(HttpServletRequest request, @RequestBody BookingDto booking) { BaseApiResponse result = new BaseApiResponse<>(); @@ -394,7 +391,6 @@ public class BookingController { String token = request.getHeader("Authorization"); String userId = JWT.decode(token).getClaim("login_id").asString(); - String groupName = JWT.decode(token).getClaim("group_name").asString(); BaseApiResponse serviceResult = bookingService.changeMiscCharge(bookingId,misCharge,userId); if(serviceResult.isSuccess()) { @@ -409,7 +405,7 @@ public class BookingController { } @OperationLog(actionName="Approve Waitlist Booking", eventCode = "waitlist_booking_approve") - @RequestMapping(value = "/app/booking/approve_waitlist", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/booking/approve_waitlist", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> approveWaitlistBooking(HttpServletRequest request, @RequestBody BookingDto booking) { BaseApiResponse result = new BaseApiResponse<>(); @@ -427,7 +423,7 @@ public class BookingController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/app/bookings/search_optimize", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/bookings/search_optimize", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> recordSearchOptimize(HttpServletRequest request, @RequestBody @Valid BookingSearchRequest searchRequest) throws Exception { LogUtil.info(BookingService.class,"[recordSearch] start recordSearch "); @@ -700,7 +696,7 @@ public class BookingController { return allPropertiesResults; } - @RequestMapping(value = "/web/booking/get_page", produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/booking/get_page", produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> getBookingByPage( HttpServletRequest request, @RequestBody(required=true) BookingQueryRequest queryFilter) throws Exception { @@ -750,7 +746,7 @@ public class BookingController { return result; } - @RequestMapping(value = "/web/bookings/search", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/bookings/search", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> webRecordSearch(HttpServletRequest request, @RequestBody WebRecordSearchRequest params) throws Exception { // requireAnyWebPermission(request, "00501_01", "00401_01", "00402_01"); List list = getWebRecordSearch(request, params); diff --git a/src/main/java/com/mgm/mcrm/booking/controllers/WaitlistController.java b/src/main/java/com/mgm/mcrm/booking/controllers/WaitlistController.java index 7c3c1d8..a905d5e 100644 --- a/src/main/java/com/mgm/mcrm/booking/controllers/WaitlistController.java +++ b/src/main/java/com/mgm/mcrm/booking/controllers/WaitlistController.java @@ -12,12 +12,12 @@ import com.mgm.mcrm.logging.services.SysLogOperationService; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController -@CrossOrigin(origins = "*") @RequestMapping("/api") public class WaitlistController { @@ -27,7 +27,7 @@ public class WaitlistController { private SysLogOperationService sysAuditLogService; @OperationLog(eventCode="booking_update",actionName="Update Waitlist Booking Status") - @RequestMapping(value = "/web/waitlist/update_waitlist_status", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/waitlist/update_waitlist_status", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> updateWaitlistStatus(HttpServletRequest request, @RequestBody Bookings booking) throws Exception { BaseApiResponse result = new BaseApiResponse<>(); @@ -59,7 +59,7 @@ public class WaitlistController { @OperationLog(eventCode="booking_update",actionName="Update Waitlist Batch Booking Status") - @RequestMapping(value = "/web/waitlist/update_batch_waitlist_status", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/waitlist/update_batch_waitlist_status", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> updateBatchWaitlistStatus(HttpServletRequest request, @RequestBody WebBatchUpdateWaitlistRequest params) throws Exception { BaseApiResponse result = new BaseApiResponse<>(); diff --git a/src/main/java/com/mgm/mcrm/booking/dtos/BookingDto.java b/src/main/java/com/mgm/mcrm/booking/dtos/BookingDto.java index 48545a8..add8369 100644 --- a/src/main/java/com/mgm/mcrm/booking/dtos/BookingDto.java +++ b/src/main/java/com/mgm/mcrm/booking/dtos/BookingDto.java @@ -2,6 +2,7 @@ package com.mgm.mcrm.booking.dtos; import com.alibaba.fastjson.JSONArray; import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; import com.mgm.mcrm.common.dtos.mgm_api.PlayerProfileEntry; import lombok.Data; @@ -21,10 +22,19 @@ public class BookingDto { private String memberTitle; private String memberFirstName; private String memberLastName; + @JsonProperty("checkinDate") @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") private Date checkinDate; + @JsonProperty("checkoutDate") @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") private Date checkoutDate; + + @JsonProperty("checkInDate") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date checkInDate; + @JsonProperty("checkOutDate") + @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") + private Date checkOutDate; private Long propertyId; private Long roomTypeId; private String dlProfileId; @@ -117,6 +127,7 @@ public class BookingDto { private String roomEmailName; private String eta; private String etaTime; + private String guaranteeType; private String changeCheckinDate; private String changeCheckoutDate; private String dlChannelId; @@ -140,7 +151,7 @@ public class BookingDto { private String dlPropertyId; private String dlRoomTypeId; private String specialCode; - private Integer DepositType; + private Integer depositType; private Integer isPost; private List extendDate; private String checkInDateVal; @@ -161,4 +172,18 @@ public class BookingDto { private BookingCancelInfo cancelInfo; private String miscCharge; + + public void setCheckOOutDate(Date checkOOutDate) { + this.checkOutDate = checkOOutDate; + } + public Date getCheckOOutDate() { + return checkOutDate; + } + + public void setCheckIInDate(Date checkIInDate) { + this.checkInDate = checkIInDate; + } + public Date getCheckIInDate() { + return checkInDate; + } } diff --git a/src/main/java/com/mgm/mcrm/booking/dtos/daylight/requests/createReservation/IndividualDTO.java b/src/main/java/com/mgm/mcrm/booking/dtos/daylight/requests/createReservation/IndividualDTO.java index 2fcea84..6d2870c 100644 --- a/src/main/java/com/mgm/mcrm/booking/dtos/daylight/requests/createReservation/IndividualDTO.java +++ b/src/main/java/com/mgm/mcrm/booking/dtos/daylight/requests/createReservation/IndividualDTO.java @@ -5,6 +5,7 @@ import com.mgm.mcrm.booking.enums.ProfileTierEnums; import lombok.Data; import org.apache.commons.lang3.StringUtils; +import java.util.Arrays; import java.util.List; @Data @@ -53,18 +54,22 @@ public class IndividualDTO { IndividualDTO.IndividualDetailsDTO newProfileDetail = new IndividualDTO.IndividualDetailsDTO(); IndividualDTO.MembershipDTO membershipDto = new IndividualDTO.MembershipDTO(); - String[] s = playerName.split(" "); + String[] lastAndFirst = playerName.split(","); // lastname, firstname - if (s.length == 1) { - String theName = s[0]; - String substring = theName.substring(0, 1); - String substring2 = theName.substring(1); + if (lastAndFirst.length == 1) { + lastAndFirst = lastAndFirst[0].split(","); // user可能输入了中文的逗号 + } - newProfileDetail.setFirstName(substring2); - newProfileDetail.setLastName(substring); + if (lastAndFirst.length == 1) { + String theName = lastAndFirst[0]; + String lastname = theName.substring(0, 1).trim(); + String firstname = theName.substring(1).trim(); + + newProfileDetail.setFirstName(firstname); + newProfileDetail.setLastName(lastname); } else { - newProfileDetail.setFirstName(s[0]); - newProfileDetail.setLastName(s[s.length - 1]); + newProfileDetail.setFirstName(lastAndFirst[1].trim()); + newProfileDetail.setLastName(lastAndFirst[0].trim()); } membershipDto.setNumber(playerId); @@ -98,18 +103,22 @@ public class IndividualDTO { IndividualDTO newProfileDto = new IndividualDTO(); IndividualDTO.IndividualDetailsDTO newProfileDetail = new IndividualDTO.IndividualDetailsDTO(); - String[] s = playerName.split(" "); + String[] lastAndFirst = playerName.split(","); // lastname, firstname - if (s.length == 1) { - String theName = s[0]; - String substring = theName.substring(0, 1); - String substring2 = theName.substring(1); + if (lastAndFirst.length == 1) { + lastAndFirst = lastAndFirst[0].split(","); // user可能输入了中文的逗号 + } - newProfileDetail.setFirstName(substring2); - newProfileDetail.setLastName(substring); + if (lastAndFirst.length == 1) { + String theName = lastAndFirst[0]; + String lastname = theName.substring(0, 1).trim(); + String firstname = theName.substring(1).trim(); + + newProfileDetail.setFirstName(firstname); + newProfileDetail.setLastName(lastname); } else { - newProfileDetail.setFirstName(s[0]); - newProfileDetail.setLastName(s[s.length - 1]); + newProfileDetail.setFirstName(lastAndFirst[1].trim()); + newProfileDetail.setLastName(lastAndFirst[0].trim()); } newProfileDto.setDetails(newProfileDetail); diff --git a/src/main/java/com/mgm/mcrm/booking/mappers/BookingMapper.java b/src/main/java/com/mgm/mcrm/booking/mappers/BookingMapper.java index c643b1e..6af32aa 100644 --- a/src/main/java/com/mgm/mcrm/booking/mappers/BookingMapper.java +++ b/src/main/java/com/mgm/mcrm/booking/mappers/BookingMapper.java @@ -86,4 +86,7 @@ public interface BookingMapper { BookingDto changeDateRequestToDto(TodoChangeDateRequest request); TodoChangeDateRequest bookingDtoToChangeRequest(BookingDto param); + + @Mapping(target = "dateList", source = "dateList", qualifiedByName = "jsonToDateList") + TodoChangeDateRequest bookingToChangeRequest(Bookings param); } diff --git a/src/main/java/com/mgm/mcrm/booking/models/OperaDBBooking.java b/src/main/java/com/mgm/mcrm/booking/models/OperaDBBooking.java index 48c5c89..e5de415 100644 --- a/src/main/java/com/mgm/mcrm/booking/models/OperaDBBooking.java +++ b/src/main/java/com/mgm/mcrm/booking/models/OperaDBBooking.java @@ -2,6 +2,7 @@ package com.mgm.mcrm.booking.models; import jakarta.persistence.*; +import lombok.Data; import java.sql.Date; @@ -45,7 +46,7 @@ import java.sql.Date; procedureName = "CheckDBConnection" ) }) - +@Data public class OperaDBBooking { @Id @@ -97,150 +98,7 @@ public class OperaDBBooking { private String roomNo; @Column(name="Is_Queue") private String isQueue; - - public String getConfirmationNo() { - return confirmationNo; - } - public void setConfirmationNo(String confirmationNo) { - this.confirmationNo = confirmationNo; - } - public String getRoomType() { - return roomType; - } - public void setRoomType(String roomType) { - this.roomType = roomType; - } - public String getRoomTypeDesc() { - return roomTypeDesc; - } - public void setRoomTypeDesc(String roomTypeDesc) { - this.roomTypeDesc = roomTypeDesc; - } - public String getArrivalDate() { - return arrivalDate; - } - public void setArrivalDate(String arrivalDate) { - this.arrivalDate = arrivalDate; - } - public String getArrivalTime() { - return arrivalTime; - } - public void setArrivalTime(String arrivalTime) { - this.arrivalTime = arrivalTime; - } - public String getDepartureDate() { - return departureDate; - } - public void setDepartureDate(String departureDate) { - this.departureDate = departureDate; - } - public String getDepartureTime() { - return departureTime; - } - public void setDepartureTime(String departureTime) { - this.departureTime = departureTime; - } - public String getResvStatus() { - return resvStatus; - } - public void setResvStatus(String resvStatus) { - this.resvStatus = resvStatus; - } - public String getBlockCode() { - return blockCode; - } - public void setBlockCode(String blockCode) { - this.blockCode = blockCode; - } - public String getCurrentBalance() { - return currentBalance; - } - public void setCurrentBalance(String currentBalance) { - this.currentBalance = currentBalance; - } - public String getOperaProfileId() { - return operaProfileId; - } - public void setOperaProfileId(String operaProfileId) { - this.operaProfileId = operaProfileId; - } - public String getResvNameId() { - return resvNameId; - } - public void setResvNameId(String resvNameId) { - this.resvNameId = resvNameId; - } - public String getRateCode() { - return rateCode; - } - public void setRateCode(String rateCode) { - this.rateCode = rateCode; - } - public String getAdults() { - return adults; - } - public void setAdults(String adults) { - this.adults = adults; - } - public String getChildren() { - return children; - } - public void setChildren(String children) { - this.children = children; - } - public String getMarketSegment() { - return marketSegment; - } - public void setMarketSegment(String marketSegment) { - this.marketSegment = marketSegment; - } - public String getSourceCode() { - return sourceCode; - } - public void setSourceCode(String sourceCode) { - this.sourceCode = sourceCode; - } - public String getSourceName() { - return sourceName; - } - public void setSourceName(String sourceName) { - this.sourceName = sourceName; - } - public String getResort() { - return resort; - } - public void setResort(String resort) { - this.resort = resort; - } - public String getBooker() { - return booker; - } - public void setBooker(String booker) { - this.booker = booker; - } - public String getExpDate() { - return expDate; - } - public void setExpDate(String expDate) { - this.expDate = expDate; - } - public String getProfileExpDate() { - return profileExpDate; - } - public void setProfileExpDate(String profileExpDate) { - this.profileExpDate = profileExpDate; - } - public String getRoomNo() { - return roomNo; - } - public void setRoomNo(String roomNo) { - this.roomNo = roomNo; - } - public String getIsQueue() { - return isQueue; - } - public void setIsQueue(String isQueue) { - this.isQueue = isQueue; - } + @Column(name="Misc_Charge") + private String miscCharge; } diff --git a/src/main/java/com/mgm/mcrm/booking/repositories/BookingDao.java b/src/main/java/com/mgm/mcrm/booking/repositories/BookingDao.java index 039a4fa..95165b1 100644 --- a/src/main/java/com/mgm/mcrm/booking/repositories/BookingDao.java +++ b/src/main/java/com/mgm/mcrm/booking/repositories/BookingDao.java @@ -83,15 +83,18 @@ public interface BookingDao extends JpaRepository { + "and((checkin_date<=?4 and checkout_date>?4))and approve_status in('N') and section_id is not null",nativeQuery=true) int getPickupBooking(Long propertyId,Long roomId,Long blockId,Date date); - @Query(value="select * from bookings where confirmation_no =?1 and approve_status not like '%W%' and approve_status != 'C' and property_id = ?2",nativeQuery=true) + @Query(value="select * from bookings where confirmation_no =?1 and approve_status = 'N' and property_id = ?2",nativeQuery=true) Bookings getByConfNoForETA(String confNo,Long propertyId); @Query(value = "select * from bookings where confirmation_no =?1", nativeQuery = true) Bookings getByConfNo(String confNo); - @Query(value = "select Top(1) * from bookings where dl_external_no is not null and ltrim(rtrim(dl_external_no)) = ltrim(rtrim(?1)) order by id asc", nativeQuery = true) + @Query(value = "select Top(1) * from bookings where dl_external_no is not null and dl_external_no = ?1 order by id asc", nativeQuery = true) Bookings getByDlExternalNo(String dlExternalNo); @Query(value = "select Top(1) * from bookings where property_id is not null and confirmation_no = ?1 order by id asc", nativeQuery = true) Bookings findFirstByConfirmationNoAndPropertyIdIsNotNull(String confirmationNo); + + @Query(value = "select b.category from Bookings b where b.id = :id") + String findCategoryById(Long id); } diff --git a/src/main/java/com/mgm/mcrm/booking/services/BookingService.java b/src/main/java/com/mgm/mcrm/booking/services/BookingService.java index 215dd6c..dac9e54 100644 --- a/src/main/java/com/mgm/mcrm/booking/services/BookingService.java +++ b/src/main/java/com/mgm/mcrm/booking/services/BookingService.java @@ -29,6 +29,7 @@ import com.mgm.mcrm.basesetting.property.repositories.PropertyDao; import com.mgm.mcrm.basesetting.property.services.PropertyCache; import com.mgm.mcrm.basesetting.room.models.*; import com.mgm.mcrm.basesetting.room.repositories.*; +import com.mgm.mcrm.basesetting.team.repositories.SectionDao; import com.mgm.mcrm.booking.dtos.BookingCancelInfo; import com.mgm.mcrm.booking.dtos.BookingDateEntry; import com.mgm.mcrm.booking.dtos.BookingDto; @@ -50,6 +51,7 @@ import com.mgm.mcrm.common.constants.SysConstants; import com.mgm.mcrm.common.dtos.BaseApiResponse; import com.mgm.mcrm.common.dtos.DataTableResult; import com.mgm.mcrm.common.dtos.EumnApprovalStatus; +import com.mgm.mcrm.common.dtos.config.ApiGatewaySetting; import com.mgm.mcrm.common.dtos.mgm_api.ApproverRecordResult; import com.mgm.mcrm.common.dtos.mgm_api.ApproverResult; import com.mgm.mcrm.common.dtos.mgm_api.PlayerProfileEntry; @@ -78,7 +80,6 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Strings; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; @@ -177,11 +178,15 @@ public class BookingService { @Autowired private BookingMapper bookingMapper; - @Value("${daylight.system-id}") - private String daylightSystemId; + @Resource + private ApiGatewaySetting apiGatewaySetting; @Resource private OperaDBBookingService operaDBBookingService; + @Resource + private SectionDao sectionDao; + + private static final Map properties = new HashMap<>(); public BaseApiResponse> createBooking(BookingDto param, HttpServletRequest request, UserInfoApiRecord userInfo, String category) throws Exception { @@ -259,10 +264,7 @@ public class BookingService { boolean submitTag = dateItem.isSubmit(); if (!submitTag) { - result.setSuccess(false); - result.setData(null); - result.setMessage("submitTag is false"); - return result; + continue; } List dateArr = dateItem.getDate(); @@ -362,8 +364,7 @@ public class BookingService { booking = getRoomType(booking, roomId, checkinDate, checkinDate, bedTypeMasterGroupId); roomTypeId = booking.getRoomTypeId(); - - CfgTiers tier = tierDao.findByTierNameAndCategory(member.getTierName(), property.getCategory()); + CfgTiers tier = tierDao.findByTierNameAndCategory(member.getTierName(), "OPERA"); CfgItemInventory item = new CfgItemInventory(); if (tier != null && roomTypeId != null) { item = itemDao.findByTierIdAndRoomId(tier.getId(), roomId); @@ -606,10 +607,7 @@ public class BookingService { boolean submitTag = dateItem.isSubmit(); if (!submitTag) { - result.setSuccess(false); - result.setData(null); - result.setMessage("submitTag is false"); - return result; + continue; } List dateArr = dateItem.getDate(); @@ -669,7 +667,15 @@ public class BookingService { booking.setBookingTypeId(bookingTypeId); booking.setMember(JSONObject.toJSONString(member)); booking.setCategory(property.getCategory()); - booking.setSectionName(sectionName); + + if (StringUtils.isNotBlank(sectionName)) { + booking.setSectionName(sectionName); + } else { + Bookings finalBooking = booking; + sectionDao.findById(sectionId).ifPresent(section -> { + finalBooking.setSectionName(section.getName()); + }); + } // 设置etaTime if (StringUtils.isNotBlank(etaTime)) @@ -719,7 +725,7 @@ public class BookingService { roomTypeId = booking.getRoomTypeId(); - CfgTiers tier = tierDao.findByTierNameAndCategory(member.getTierName(), property.getCategory()); + CfgTiers tier = tierDao.findByTierNameAndCategoryAndPropertyId(member.getTierName(), property.getCategory(), propertyId); CfgItemInventory item = new CfgItemInventory(); if (tier != null && roomTypeId != null) { item = itemDao.findByTierIdAndRoomId(tier.getId(), roomId); @@ -932,8 +938,14 @@ public class BookingService { Long bookingTypeId = booking.getBookingTypeId(); CfgBookingTypes bookingType = bookingTypeDao.findById(bookingTypeId).orElse(null); String bookingTypeName = bookingType.getName(); + CfgBlockCodes blockCodeById; + //该方法专门用来处理Daylight Booking,如果后面不需要Category判断可以直接去掉 + if (Strings.CS.equals(booking.getCategory(),"DAYLIGHT")){ + blockCodeById = blockCodeDao.findByDlBlockId(booking.getDlBlockId()); + }else{ + blockCodeById = blockCodeDao.findById(booking.getBlockId()).orElse(null); + } - CfgBlockCodes blockCodeById = blockCodeDao.findById(booking.getBlockId()).orElse(null); // if (blockCodeById == null) { // booking.setApproveStatus("F"); @@ -1050,18 +1062,6 @@ public class BookingService { roomTypeId ); -// if (CollectionUtils.isEmpty(daylightViewList)) { -// booking.setApproveStatus("F"); -// booking.setErrorMessage("Daylight view not found"); -// bookingDao.save(booking); -// log.warn("Not Found DaylightView, sectionId={}, propertyId={}, bookingTypeId={}, roomTypeId={}", -// booking.getSectionId(), booking.getPropertyId(), booking.getBookingTypeId(), roomTypeId); -// processResult.setSuccess(false); -// processResult.setMessage("Not Found DaylightView"); -// processResult.setData(resultBookings); -// return processResult; -// } - CfgDaylightView daylightView = daylightViewList.getFirst(); CfgRateCodes rateCode = rateCodeDao.findById(daylightView.getRateCodeId()).orElse(null); @@ -1304,15 +1304,12 @@ public class BookingService { } else { log.info("Sharer={} exists the playerId={}, tierName={}", sharerName, sharerId, sharer.getString("tierName")); - BaseApiResponse sharerDetail = - playerService.findPlayerProfileByPlayerIdAndUserId(sharerId, userId); - individualDTO = IndividualDTO.buildNewProfileWithPlayerId( sharerId, sharerName, "shamem_" + i, "sha_" + i, - sharerDetail.getData().getTierName() + sharer.getString("tierName") ); } @@ -1357,6 +1354,9 @@ public class BookingService { if (booking.getWaitlistId() != null) { customFields.put("App_Rea", "APP"); customFields.put("Approver", booking.getApprovedBy()); + } else if (booking.getApprovalBookingId() != null) { + customFields.put("App_Rea", "APP"); + customFields.put("Approver", String.valueOf(booking.getApprovedId())); } customFieldDTO.setLocation("ReservationHeader"); @@ -1375,14 +1375,14 @@ public class BookingService { String operaComment; String stayDateForOpera = DateTimeUtil.formatOperaCommentStayDateRange( checkinDate, checkoutDate, booking.getNumOfNight()); - + if ("CREATE_BOOKING".equalsIgnoreCase(type)) { if ("IM".equalsIgnoreCase(bookingTypeName)) { operaComment = buildIMComment(booking); } else { String miscCharge = booking.getMiscCharge(); - // 生成给 OPERA 的 comment:根据 miscCharge 是否为空决定是否包含 INC.CHRG$ 相关信息 + // 生成给 DAYLIGHT 的 comment:根据 miscCharge 是否为空决定是否包含 INC.CHRG$ 相关信息 if (miscCharge == null || miscCharge.trim().isEmpty()) { operaComment = "(RM)COMP ..APP\n\n"+ stayDateForOpera+",OD,"+userId; } else { @@ -1444,6 +1444,8 @@ public class BookingService { return processResult; } + // 下面的流程是create booking success的 + String reservationId = createResponse.getData().getString("reservationId"); String confirmationNumber = createResponse.getData().getString("confirmationNumber"); @@ -1452,17 +1454,41 @@ public class BookingService { booking.setDlAccountId(daylightReservationByDlId.getAccountId()); booking.setDlReservationId(reservationId); -// booking.setConfirmationNo(confirmationNumber); + booking.setConfirmationNo(confirmationNumber); // booking.setLastOperaStatus("RESERVED"); // booking.setApproveStatus("N"); // -// bookingDao.save(booking); + bookingDao.save(booking); + + String roomName = booking.getRoom(); + + if (roomName.startsWith("R2") || roomName.startsWith("Y2")) { + CfgSettings emailAddress = settingService.getSettingByKey(SysConstants.MGM_EMAIL_ADDRESS); + BookingsView originBookingView = bookingViewDao.findById(booking.getId()).orElse(null); + originBookingView = this.getEmailInfo(originBookingView,booking.getCreatedBy()).getData(); + + Email email = new Email(); + email.setBookId(booking.getId()); + email.setCreatedAt(new Date()); + email.setCreatedBy(booking.getCreatedBy()); + email = SendEmail.sendCreateR2Y2NewEmail(booking, originBookingView,email,userId,booking.getCreatedByName(), false, true, emailAddress.getKeyValues()); + + if (email.getStatus() == 1) { + emailDao.save(email); + processResult.setSuccess(true); + }else{ + processResult.setSuccess(false); + processResult.setMessage("Send Email Failed"); + processResult.setLogMessage("Send Email Failed"); + } + } dlService.createDaylightCompRule( daylightReservationByDlId.getAccountId(), daylightReservationByDlId.getProfileId(), property.getDlPropertyId(), userId, + booking.getRoom(), checkinDateStr, checkoutDateStr, daylightViewList, @@ -1601,10 +1627,10 @@ public class BookingService { ReservationRequestDTO.ExternalIdDTO externalIdDTO2 = new ReservationRequestDTO.ExternalIdDTO(); ReservationRequestDTO.IdentityDTO identityDTO = new ReservationRequestDTO.IdentityDTO(); - externalIdDTO1.setSystemId(daylightSystemId); + externalIdDTO1.setSystemId(apiGatewaySetting.getDaylightSystemId()); externalIdDTO1.setNumber(externalId); - externalIdDTO2.setSystemId(daylightSystemId); + externalIdDTO2.setSystemId(apiGatewaySetting.getDaylightSystemId()); externalIdDTO2.setNumber(externalId); identityDTO.setExternalId(externalIdDTO1); @@ -2061,8 +2087,8 @@ public class BookingService { String bookingStatus = extendDate.getBookingStatus(); Date originCheckinDate = DateTimeUtil.getDateFromString( String.valueOf(dateArr.getFirst()), "yyyy-MM-dd"); - Date checkinDate = param.getCheckinDate(); - Date checkoutDate = param.getCheckoutDate(); + Date checkinDate = param.getCheckIInDate(); + Date checkoutDate = param.getCheckOOutDate(); String numOfNight = DateTimeUtil.getDiffDate(checkinDate, checkoutDate, "day"); BookingUserEntry user = param.getUser(); @@ -2081,8 +2107,16 @@ public class BookingService { booking = new Bookings(); BeanUtils.copyProperties(originalBooking, booking, "id"); booking.setId(null); + } else if (segmentId != null) { + // Other Property extension only updates requestedCoDate on the original booking. + // Use a working copy here so the common extension preparation below does not + // dirty the JPA-managed original checkoutDate/dateList before editOtherBooking. + booking = new Bookings(); + BeanUtils.copyProperties(originalBooking, booking); } else { - booking = originalBooking; + booking = new Bookings(); + BeanUtils.copyProperties(originalBooking, booking, "id"); + booking.setId(null); } boolean isShareBooking = !StringUtils.isEmpty(originalBooking.getSharer()) @@ -2183,6 +2217,7 @@ public class BookingService { originBook.setSharerName(sharerString.toString()); originBook.setMemberLastName(player.getName()); booking.setMemberLastName(player.getName()); + Date checkOutDate = DateTimeUtil.getDateFromString(param.getChangeCheckoutDate(), "yyyy-MM-dd"); if (submitTag && !bookingStatus.equals("PN")) { @@ -2191,7 +2226,8 @@ public class BookingService { if (segmentId != null) { // NON MGM Booking - Bookings oBook = editOtherBooking(bookId, checkinDate, checkoutDate, segmentId); + booking = new Bookings(); + Bookings oBook = editOtherBooking(bookId, checkinDate, checkOutDate, segmentId); resultBookings.add(oBook); processResult.setSuccess(true); processResult.setData(resultBookings); @@ -2227,7 +2263,7 @@ public class BookingService { originBook.getCheckoutDate() ); booking.setCheckoutDate( - DateTimeUtil.getDateFromString(param.getChangeCheckoutDate(),"yyyy-MM-dd")); + checkOutDate); booking.setApprovalBookingId(String.valueOf(originBook.getId())); booking.setOtherFollowUpStatusId(2L); Bookings newBooking = bookingDao.save(booking); @@ -2250,7 +2286,7 @@ public class BookingService { originBook.getCheckoutDate() ); booking.setCheckoutDate( - DateTimeUtil.getDateFromString(param.getChangeCheckoutDate(),"yyyy-MM-dd")); + checkOutDate); Bookings newBooking = bookingDao.save(booking); @@ -2367,16 +2403,47 @@ public class BookingService { booking.setMemberLastName(player.getName()); if(submitTag && !bookingStatus.equals("PN") && !isWaitlist) { - + //判断SourceCode 如果为OD 或 VMTK 且没有Sharer 直接extend TodoChangeDateRequest changeRequest = bookingMapper.bookingDtoToChangeRequest(param); BaseApiResponse> result = this.daylightReservationChangeDate(changeRequest, player, true); + if (result.isSuccess()) { processResult.setSuccess(true); - processResult.setData(resultBookings); + processResult.setData(result.getData()); } else { processResult.setSuccess(false); - processResult.setCode(result.getMessage()); + processResult.setMessage(result.getMessage()); + } + + if (result.isSuccess()) { + if ((!booking.getSourceCode().isEmpty() && !Strings.CS.containsAny(booking.getSourceCode(),"OD","VMKT")) + || + (booking.getSharer() != null && !Strings.CS.equals(booking.getSharer(),"[]"))) { + //send email instead of sync other booking + Date now = new Date(); + CfgSettings emailAddress = settingService.getSettingByKey(SysConstants.MGM_EMAIL_ADDRESS); + BookingsView originBookingView = bookingViewDao.findById(bookId).orElse(null); + originBookingView = this.getEmailInfo(originBookingView,originBookingView.getCreatedBy()).getData(); + booking.setCheckoutDate( + DateTimeUtil.getDateFromString(param.getChangeCheckoutDate(), "yyyy-MM-dd")); + + Email email = new Email(); + email.setBookId(booking.getId()); + email.setCreatedAt(now); + email.setCreatedBy(booking.getCreatedBy()); + email = SendEmail.sendExtendEmail(booking, originBookingView,email,userId,userName,emailAddress.getKeyValues()); + + if(email.getStatus()==1) { + emailDao.save(email); + processResult.setSuccess(true); + processResult.setData(resultBookings); + }else{ + processResult.setSuccess(false); + processResult.setMessage("Send Email Failed"); + processResult.setLogMessage("Send Email Failed"); + } + } } }else if (isApproval){ booking.setCheckinDate( @@ -2432,6 +2499,7 @@ public class BookingService { Date originCheckinDate = DateTimeUtil.getDateFromString(dateArr.getFirst(), "yyyy-MM-dd"); Date checkinDate; Date checkoutDate; + String userId = param.getUser()!=null? param.getUser().getUserId():null; Bookings booking = bookingDao.findById(bookId).orElse(null); @@ -2471,9 +2539,24 @@ public class BookingService { booking.setCheckinDate(checkinDate); booking.setRevisionOf(null); } - }else{ + } else { checkinDate = param.getCheckinDate(); checkoutDate = param.getCheckoutDate(); + + String newComment = buildDaylightAmendComment(booking, checkinDate, checkoutDate); + booking.setOperaComment(newComment); + + CfgProperties pp = propertyDao.findById(booking.getPropertyId()).orElse(null); + BaseApiResponse response = + dlService.daylightGetNoteListById(pp.getDlPropertyId(), booking.getDlReservationId()); + ReservationNoteResponse noteData = response.getData(); + + dlService.daylightUpdateReservationNote( + pp.getDlPropertyId(), + booking.getDlReservationId(), + noteData.getNoteId(), + newComment, + noteData.getVersion()); } String checkinDateStr = DateTimeUtil.getDateByFormat(checkinDate, "yyyy-MM-dd"); String checkoutDateStr = DateTimeUtil.getDateByFormat(checkoutDate, "yyyy-MM-dd"); @@ -2496,7 +2579,7 @@ public class BookingService { booking.setNumOfNight(Integer.valueOf(numOfNight)); booking.setSharer(sharer); booking.setUpdatedAt(createDate); - booking.setUpdatedBy(param.getUserId()); + booking.setUpdatedBy(userId); booking.setCreatedAt(createDate); if (isExtend) { @@ -2551,57 +2634,67 @@ public class BookingService { CfgProperties property = propertyDao.findById(booking.getPropertyId()).orElse(null); - BaseApiResponse result = - dlService.daylightChangeReservationByDlId(bookId, checkinDateStr, checkoutDateStr, property.getDlPropertyId()); - -// BaseApiResponse> result = this.callDaylightUpsertReservation(newBooking, "CHANGE_DATE"); + BaseApiResponse result; + String originalCheckinDateStr = DateTimeUtil.getDateByFormat(originBook.getCheckinDate(), "yyyy-MM-dd"); + if (!Strings.CS.equals(checkinDateStr, originalCheckinDateStr)) { + result = this.callDaylightUpsertReservation(newBooking, "CHANGE_DATE"); + } else { + result = dlService.daylightChangeReservationByDlId( + bookId, checkinDateStr, checkoutDateStr, property.getDlPropertyId()); + } // Thread.sleep(2000L); // if (booking.getDlBlockId() != null) // dlService.daylightReservationSetBlock(booking.getDlReservationId(), property.getDlPropertyId(), booking.getDlBlockId()); - // TODO Need to create a new comp rule when extend the booking by Mauve 20260827 - List daylightViewList = daylightViewDao.findBySectionPropertyBookingTypeRoomType( - booking.getSectionId(), - booking.getPropertyId(), - booking.getBookingTypeId(), - booking.getRoomTypeId() - ); - CfgDaylightView daylightViewRoomOnly = null; - if(daylightViewList!=null){ - for (CfgDaylightView d : daylightViewList) { - if (d.getCompRuleCategory() == 1) { - daylightViewRoomOnly = d; - } - } - } + // Need to create a new comp rule when extend the booking by Mauve 20260827 + CfgBookingTypes cfgBookingTypes = + bookingTypeDao.findById(booking.getBookingTypeId()).orElse(null); - DaylightReservationResponse daylightReservationByDlId = - dlService.getDaylightReservationByDlId(booking.getDlReservationId(), property.getDlPropertyId()); + if (!"IM".equalsIgnoreCase(cfgBookingTypes.getName()) && isExtend) { - BaseApiResponse compRuleListResponse = dlService.daylightGetCompRulesByAccount(property.getDlPropertyId(),daylightReservationByDlId.getAccountId()); - if(compRuleListResponse.isSuccess()&&daylightViewRoomOnly!=null){ - JSONArray compRuleList = compRuleListResponse.getData(); - boolean hasSameUser = false; - for (int i = 0; i < compRuleList.size(); i++) { - JSONObject one = compRuleList.getJSONObject(i); - String approverId = one.getString("approverId"); - if(Strings.CS.equals(approverId,param.getUserId()) - || Strings.CS.equals(approverId,daylightViewRoomOnly.getApprover()) ){ - hasSameUser = true; - } - } - if(!hasSameUser) dlService.createDaylightNewRoomOnlyCompRule( - daylightReservationByDlId.getAccountId(), - daylightReservationByDlId.getProfileId(), - property.getDlPropertyId(), - param.getUserId(), - checkinDateStr, - checkoutDateStr, - daylightViewRoomOnly + + List daylightViewList = daylightViewDao.findBySectionPropertyBookingTypeRoomType( + booking.getSectionId(), + booking.getPropertyId(), + booking.getBookingTypeId(), + booking.getRoomTypeId() ); - } + CfgDaylightView daylightViewRoomOnly = null; + if(daylightViewList!=null){ + for (CfgDaylightView d : daylightViewList) { + if (Objects.equals(d.getCompRuleCategory(), 1)) { + daylightViewRoomOnly = d; + } + } + } + DaylightReservationResponse daylightReservationByDlId = + dlService.getDaylightReservationByDlId(booking.getDlReservationId(), property.getDlPropertyId()); + + BaseApiResponse compRuleListResponse = dlService.daylightGetCompRulesByAccount(property.getDlPropertyId(),daylightReservationByDlId.getAccountId()); + if(compRuleListResponse.isSuccess()&&daylightViewRoomOnly!=null&&userId!=null){ + JSONArray compRuleList = compRuleListResponse.getData(); + boolean hasSameUser = false; + for (int i = 0; i < compRuleList.size(); i++) { + JSONObject one = compRuleList.getJSONObject(i); + String approverId = one.getString("approverId"); + if(Strings.CS.equals(approverId,userId) + || Strings.CS.equals(approverId,daylightViewRoomOnly.getApprover()) ){ + hasSameUser = true; + } + } + if(!hasSameUser) dlService.createDaylightNewRoomOnlyCompRule( + daylightReservationByDlId.getAccountId(), + daylightReservationByDlId.getProfileId(), + property.getDlPropertyId(), + userId, + checkinDateStr, + checkoutDateStr, + daylightViewRoomOnly + ); + } + } bookingDao.save(booking); @@ -2641,10 +2734,53 @@ public class BookingService { oBooking.setRequestedCoDate(checkoutDate); oBooking.setOtherFollowUpStatusId(Long.valueOf("2")); bookingDao.save(oBooking); - otherBookingService.processOtherAllotment(oBooking.getRoomId(), checkoutDate, - checkinDate, oBooking.getPropertyId(), oBooking.getSegmentId()); + + // otherBookingService.processOtherAllotment(oBooking.getRoomId(), checkoutDate, + // checkinDate, oBooking.getPropertyId(), oBooking.getSegmentId()); return oBooking; } + + /** + * Extends the real Daylight reservation represented by an approved waitlist + * extension record. The waitlist row contains the requested checkout date, + * while waitlistId identifies the booking that must be sent to Daylight. + */ + public BaseApiResponse> extendDaylightWaitlistBooking(Bookings waitlistBooking, + PlayerProfileEntry player, String userId, String userName) throws Exception { + BaseApiResponse> result = new BaseApiResponse<>(); + if (waitlistBooking == null || waitlistBooking.getWaitlistId() == null) { + result.setSuccess(false); + result.setMessage("Waitlist booking does not reference an original booking"); + return result; + } + + Bookings originalBooking = bookingDao.findById(waitlistBooking.getWaitlistId()).orElse(null); + if (originalBooking == null) { + result.setSuccess(false); + result.setMessage("Original booking not found: " + waitlistBooking.getWaitlistId()); + return result; + } + if (originalBooking.getCheckinDate() == null || originalBooking.getCheckoutDate() == null + || waitlistBooking.getCheckoutDate() == null) { + result.setSuccess(false); + result.setMessage("Booking dates are incomplete"); + return result; + } + + TodoChangeDateRequest changeRequest = bookingMapper.bookingToChangeRequest(originalBooking); + if (changeRequest.getDateList() == null || changeRequest.getDateList().isEmpty()) { + changeRequest.setDateList(buildDefaultDateList( + originalBooking.getCheckinDate(), originalBooking.getCheckoutDate())); + } + // status=0 is intentional: this request is already approved, so it must + // perform an actual extension instead of creating another waitlist row. + changeRequest.setExtendDate(buildDefaultDateList( + originalBooking.getCheckinDate(), waitlistBooking.getCheckoutDate())); + changeRequest.setUserId(userId); + changeRequest.setUserName(userName); + + return daylightReservationChangeDate(changeRequest, player, true); + } public BaseApiResponse cancelWaitlistBooking(BookingDto param,HttpServletRequest request) { BaseApiResponse processResult= new BaseApiResponse<>(); @@ -3520,8 +3656,6 @@ public class BookingService { view.setSpecialName(specialName); } - String expDate = ""; - view.setDlSharerIds(daylightResultBooking.getAccompanyingGuestProfileIds()); view.setDlVersion(daylightResultBooking.getVersion()); @@ -3535,6 +3669,7 @@ public class BookingService { BaseApiResponse> profileResult = dlService.dayLightGetProfileByPlayerId(view.getPlayerId()); + String expDate; if (profileResult == null || profileResult.getData() == null || profileResult.getData().isEmpty()) { // 查不到主 guest 的 profile,expDate 直接置空 expDate = null; @@ -3767,11 +3902,10 @@ public class BookingService { createRequest.setBooking(booking); LogUtil.info(BookingService.class, "[callOWSCreateBooking] booking " + gson.toJson(booking)); - JSONObject createBookResponse = new JSONObject(); - createBookResponse = operaService.createBooking(createRequest); + JSONObject createBookResponse = operaService.createBooking(createRequest); LogUtil.info(BookingService.class, "[Reservation] createBookResponse " + createBookResponse.toJSONString()); - JSONObject errors = new JSONObject(); + JSONObject errors; boolean createProfile = booking.getOperaProfileId() == null; if (!createBookResponse.isEmpty()) { if (createBookResponse.get("internal_id") != null) { @@ -4398,20 +4532,23 @@ public class BookingService { profileIdList = operaDBService.getOperaNameIDByPlayerID(sharePlayerId); LogUtil.info(BookingService.class, "[synvBookingSharer] profileIdList " + gson.toJson(profileIdList)); -// if (profileIdList.size()>0) { -// shareBook.setOperaProfileId(Long.valueOf(profileIdList.get(0))); -// }else { -// shareBook.setOperaProfileId(null); -// } -// - String shareNameStr = Strings.CS.replace(shareName, ", ", ";"); - String[] shareNameArr = StringUtils.split(shareNameStr, ";"); - if(shareNameArr.length>1) { - shareBook.setMemberFirstName(shareNameArr[1]); - shareBook.setMemberLastName(shareNameArr[0]); + if (profileIdList.size()>0) { + shareBook.setOperaProfileId(Long.valueOf(profileIdList.get(0))); }else { - shareBook.setMemberLastName(shareName); - } + shareBook.setOperaProfileId(null); + } + + if(shareName!=null){ + String shareNameStr = Strings.CS.replace(shareName, ", ", ";"); + String[] shareNameArr = StringUtils.split(shareNameStr, ";"); + if(shareNameArr.length>1) { + shareBook.setMemberFirstName(shareNameArr[1]); + shareBook.setMemberLastName(shareNameArr[0]); + }else { + shareBook.setMemberLastName(shareName); + } + } + }else if(!StringUtils.isEmpty(shareName)) { // shareBook.setOperaProfileId(null); shareBook.setPlayerId(null); @@ -4888,7 +5025,7 @@ public class BookingService { // daylightBooking.setCheckinDate(dbBookingView.getCheckinDate()); // daylightBooking.setCheckoutDate(dbBookingView.getCheckoutDate()); // } - CfgRoomTypes daylightRoomType = roomTypeDao.findByDlRoomTypeId(daylightBooking.getDlRoomTypeId()); + CfgRoomTypes daylightRoomType = roomTypeDao.findByDlRoomTypeIdAndPropertyId(daylightBooking.getDlRoomTypeId(), daylightBooking.getPropertyId()); if (daylightRoomType != null) { daylightBooking.setRoomTypeId(daylightRoomType.getId()); daylightBooking.setRoomTypeCode(daylightRoomType.getCode()); @@ -4906,6 +5043,16 @@ public class BookingService { } } } +// +// if (dbBooking.getSectionId() == null){ +// List daylightViewList = daylightViewDao.findBySectionPropertyBookingTypeRoomType( +// dbBooking.getSectionId(), +// dbBooking.getPropertyId(), +// dbBooking.getBookingTypeId(), +// dbBooking.getRoomTypeId() +// ); +// +// } resolveDaylightBlockInfo(daylightBooking); resolveDaylightSourceCode(daylightBooking); List operaMarketCode = marketCodeDao.findByCodeAndCategory(daylightBooking.getMarketCode(), "DAYLIGHT"); @@ -5021,7 +5168,7 @@ public class BookingService { public void supplementDaylightBookingRow_Optimize(BookingDto daylightBooking, PlayerProfileEntry member, String userId) throws Exception { CfgProperties daylightProperty = propertyDao.getByDlPropertyId(daylightBooking.getDlPropertyId()); - CfgRoomTypes daylightRoomType =roomTypeDao.findByDlRoomTypeId(daylightBooking.getDlRoomTypeId()); + CfgRoomTypes daylightRoomType =roomTypeDao.findByDlRoomTypeIdAndPropertyId(daylightBooking.getDlRoomTypeId(), daylightProperty.getId()); Long propertyId = null; Long roomId= (long) -1; Long roomTypeId= (long) -1; @@ -5244,10 +5391,9 @@ public class BookingService { if (blockCode == null) { return; } + daylightBooking.setBlockCode(blockCode.getCode()); - if (blockCode.getBlockId() != null) { - daylightBooking.setBlockId(blockCode.getBlockId()); - } + daylightBooking.setBlockId(blockCode.getBlockId()); } private void resolveDaylightSourceCode(BookingDto daylightBooking) { @@ -5306,7 +5452,19 @@ public class BookingService { view.setDlAccountId(booking.getDlAccountId()); view.setDlReservationId(booking.getDlReservationId()); view.setDlExternalNo(booking.getDlExternalNo()); - view.setPropertyId(booking.getPropertyId()); + + if (Objects.isNull(properties.get(booking.getPropertyId()))) { + propertyDao.findById(booking.getPropertyId()).ifPresent( + property -> { + properties.put(booking.getPropertyId(), property); + view.setPropertyName(property.getName()); + } + ); + } else { + view.setPropertyName(properties.get(booking.getPropertyId()).getName()); + } + + view.setPropertyId(booking.getPropertyId()); view.setSectionId(booking.getSectionId()); view.setBookingTypeId(booking.getBookingTypeId() != null ? booking.getBookingTypeId().intValue() : null); return view; @@ -5380,7 +5538,7 @@ public class BookingService { } } //get room type code - CfgRoomTypes operRoomType = roomTypeDao.findByCodeAndCategory(operaBooking.getRoomTypeCode(), operaBooking.getPropertyCategory()); + CfgRoomTypes operRoomType = roomTypeDao.findByCodeAndCategory(operaBooking.getRoomTypeCode(), "OPERA"); if (operRoomType != null) { operaBooking.setRoomTypeId(operRoomType.getId()); operaBooking.setRoomTypeCode(operaBooking.getRoomTypeCode()); @@ -5466,7 +5624,7 @@ public class BookingService { getUser = true; } if (getUser) { - if (!StringUtils.isEmpty(operaBooking.getCreatedBy())) { + if (!StringUtils.isEmpty(operaBooking.getCreatedBy()) && operaBooking.getSectionId() == null) { BaseApiResponse userInfoRes = mgmUserService.findUserInfoByUserId(operaBooking.getCreatedBy()); if (userInfoRes.isSuccess()) { UserInfoApiRecord userInfo = userInfoRes.getData(); @@ -5512,9 +5670,11 @@ public class BookingService { } public void supplementOperaBookingRow_Optimize(BookingDto operaBooking, PlayerProfileEntry member, String userId) throws Exception { - CfgProperties operaProperty = propertyDao.findByHotelCode(operaBooking.getHotelCode()); + String propertyCategory = StringUtils.defaultIfBlank(operaBooking.getPropertyCategory(), "OPERA"); + CfgProperties operaProperty = propertyDao.findByHotelCodeAndCategory(operaBooking.getHotelCode(), propertyCategory); + Date now = new Date(); if (operaProperty == null) return; - CfgRoomTypes operRoomType =roomTypeDao.findByCodeAndCategory(operaBooking.getRoomTypeCode(),operaBooking.getPropertyCategory()); + CfgRoomTypes operRoomType =roomTypeDao.findByCodeAndCategory(operaBooking.getRoomTypeCode(),"OPERA"); Long propertyId =null; Long roomTypeId =(long)-1; Long roomId = (long)-1; @@ -5555,8 +5715,14 @@ public class BookingService { operaBooking.setMemberLastName(operaMemberLastName); operaBooking.setPaymentType(null); operaBooking.setApproveStatus("N"); - operaBooking.setCreatedAt(operaBooking.getDataTime()); - operaBooking.setUpdatedAt(operaBooking.getDataTime()); + + //对于OPERA Booking guarantee Type 都默认为CGTD + operaBooking.setGuaranteeType("CGTD"); + +// operaBooking.setCreatedAt(operaBooking.getDataTime()); +// operaBooking.setUpdatedAt(operaBooking.getDataTime()); + operaBooking.setCreatedAt(now); + operaBooking.setUpdatedAt(now); operaBooking.setSharer(new JSONArray().toJSONString()); operaBooking.setSharerList(new JSONArray()); operaBooking.setBookType("0"); @@ -5684,7 +5850,7 @@ public class BookingService { } if ("N".equalsIgnoreCase(currentBooking.getApproveStatus())) { - insertCommentAndMiscChargeFromDaylight(mergeBookingDto); + insertCommentAndMiscChargeFromDaylight(mergeBookingDto, currentBooking.getMiscCharge()); mergedBooking.setOperaComment(mergeBookingDto.getOperaComment()); mergedBooking.setMiscCharge(mergeBookingDto.getMiscCharge()); } @@ -5693,6 +5859,8 @@ public class BookingService { preserveDateListDepositFields(mergedBooking, currentBooking); mergedBooking.setId(existBookingId); mergedBooking.setUpdatedAt(mergeBookingDto.getDataTime()); + Date now = new Date(); + mergedBooking.setCreatedAt(now); Bookings savedBooking = bookingDao.save(mergedBooking); if (populateIds) { @@ -5840,6 +6008,9 @@ public class BookingService { boolean needSetPost = StringUtils.isEmpty(bookings.getMiscCharge()); + CfgBookingTypes cfgBookingTypes = bookingTypeDao.findById(bookings.getBookingTypeId()).orElse(null); + String bookingTypeName = cfgBookingTypes.getName(); + boolean isMock = sysSettingService.isMockReplicate(); String miscChargeValue = miscCharge == null ? "" : miscCharge.trim(); @@ -5863,8 +6034,14 @@ public class BookingService { ReservationNoteResponse noteResponse = response.getData(); String newDaylightComment = buildNewDaylightComment( - bookings.getOperaComment(), bookings.getMiscCharge(), miscChargeValue, - dateRange, bookings.getSourceCode(), userId, bookings.getCreatedBy() + bookings.getOperaComment(), + bookings.getMiscCharge(), + miscChargeValue, + dateRange, + bookings.getSourceCode(), + userId, + bookings.getCreatedBy(), + bookingTypeName ); bookings.setMiscCharge(miscChargeValue); @@ -5903,6 +6080,7 @@ public class BookingService { daylightReservationByDlId.getProfileId(), property.getDlPropertyId(), userId, + bookings.getRoom(), checkinDateStr, checkoutDateStr, daylightViewList, @@ -5931,7 +6109,7 @@ public class BookingService { if (CollectionUtils.isNotEmpty(commentList)) { OperaComment lastComment = commentList.getLast(); bookings.setOperaComment(lastComment.getComments()); - bookings.setOperaCommentId(Long.valueOf(lastComment.getResvCommentId())); + bookings.setOperaCommentId(parseOperaCommentId(lastComment.getResvCommentId())); commentType = lastComment.getCommentType(); LogUtil.info(BookingService.class, "Get Comment Success: " + lastComment.getComments() + " CommentId: " + lastComment.getResvCommentId()); } else { @@ -5968,24 +6146,57 @@ public class BookingService { return processResult; } - private String buildNewDaylightComment(String originalComment, String oldMisCharge, String newMisCharge, - String dateRange, String sourceCode, String userId, String createdBy) { + private Long parseOperaCommentId(String rawCommentId) { + if (StringUtils.isBlank(rawCommentId)) { + return null; + } + String value = rawCommentId.trim(); + try { + return Long.valueOf(value); + } catch (NumberFormatException ignored) { + try { + return Double.valueOf(value).longValue(); + } catch (NumberFormatException ex) { + LogUtil.warn(BookingService.class, "Invalid Opera comment id: " + rawCommentId); + return null; + } + } + } + + private String buildNewDaylightComment( + String originalComment, + String oldMisCharge, + String newMisCharge, + String dateRange, + String sourceCode, + String userId, + String createdBy, + String bookingTypeName + ) { return buildNewODCommentContent(originalComment, oldMisCharge, newMisCharge, - dateRange, sourceCode, userId, createdBy); + dateRange, sourceCode, userId, createdBy, bookingTypeName); } private String buildNewOperaComment(String originalComment, String oldMisCharge, String newMisCharge, String dateRange, String sourceCode, String userId, String createdBy) { String comment = buildNewODCommentContent(originalComment, oldMisCharge, newMisCharge, - dateRange, sourceCode, userId, createdBy); + dateRange, sourceCode, userId, createdBy, "Mass"); return comment.replace("\n", " ").replace("$", "$"); } /** * 构建 OD comment 的通用内容,统一使用真实换行符和美元符号。 */ - private String buildNewODCommentContent(String originalComment, String oldMisCharge, String newMisCharge, - String dateRange, String sourceCode, String userId, String createdBy) { + private String buildNewODCommentContent( + String originalComment, + String oldMisCharge, + String newMisCharge, + String dateRange, + String sourceCode, + String userId, + String createdBy, + String bookingTypeName + ) { String comment = originalComment == null ? "" : originalComment; boolean hadOldMisCharge = oldMisCharge != null && !oldMisCharge.trim().isEmpty(); @@ -5995,6 +6206,22 @@ public class BookingService { .replace(" ", "\n").replace("$", "$"); } + if ("IM".equalsIgnoreCase(bookingTypeName) && comment.startsWith("GOA")) { + if (StringUtils.isBlank(newMisCharge)) { + return comment; + } + + String amendLine = "AMEND INC.CHRG$" + newMisCharge.trim() + " AS " + userId + "..APP"; + Matcher amendMatcher = Pattern.compile("(?m)^AMEND INC\\.CHRG\\s*\\$").matcher(comment); + if (amendMatcher.find()) { + // 保留 GOA 原始说明,将最新金额记录插入已有修改记录之前。 + int insertPos = amendMatcher.start(); + return comment.substring(0, insertPos).stripTrailing() + "\n\n" + + amendLine + "\n" + comment.substring(insertPos); + } + return comment.stripTrailing() + "\n\n" + amendLine; + } + if (!newMisCharge.isEmpty() && !comment.isEmpty()) { boolean isBaseTemplate = comment.contains("(RM)COMP ..APP") && comment.contains("\n\n" + dateRange + "," + sourceCode + "," + userId) @@ -6065,8 +6292,48 @@ public class BookingService { * 延住日期段 = 原离店日 ~ 新离店日-1,例如:原离店 2026-08-09,延长至 2026-08-11 离店 → 09-10AUG * 原 comment 有标准 RM header 时,header 后保留一个空行,再插入 EXT 行。 */ - private String buildDaylightExtendComment(Bookings booking, Date originCheckoutDate, String booker) { - return buildODExtendCommentContent(booking, originCheckoutDate, booker); + private String buildDaylightExtendComment(Bookings booking, Date newCheckoutDate, String booker) { + if (booking != null && booking.getBookingTypeId() != null) { + CfgBookingTypes bookingType = bookingTypeDao.findCfgBookingTypesById(booking.getBookingTypeId()); + if (bookingType != null && "IM".equalsIgnoreCase(bookingType.getName())) { + return buildIMDaylightExtendComment(booking, newCheckoutDate, booker); + } + } + + return buildODExtendCommentContent(booking, newCheckoutDate, booker); + } + + private String buildIMDaylightExtendComment(Bookings booking, Date newCheckoutDate, String booker) { + if (booking == null || booking.getCheckoutDate() == null || newCheckoutDate == null) { + return null; + } + + String stayDate = formatExtStayDateRange(booking.getCheckoutDate(), newCheckoutDate); + String bookerName = StringUtils.defaultIfBlank(booker, booking.getBooker()); + String extLine = "EXT " + stayDate + ",OD," + bookerName + "..APP"; + String existingComment = booking.getOperaComment(); + + if (StringUtils.isBlank(existingComment)) { + return extLine; + } + + String comment = existingComment + .replace("\r\n", "\n") + .replace("\r", "\n") + .replace(" ", "\n") + .replace("$", "$"); + String goaHeader = "GOA/TBA..APP"; + int goaEndIndex = comment.indexOf(goaHeader); + + if (goaEndIndex < 0) { + return extLine + "\n\n" + comment.trim(); + } + + goaEndIndex += goaHeader.length(); + return comment.substring(0, goaEndIndex) + + "\n" + + extLine + + comment.substring(goaEndIndex); } private String buildOperaExtendComment(Bookings booking, Date originCheckoutDate, String booker) { @@ -6077,6 +6344,21 @@ public class BookingService { return comment.replace("\n", " ").replace("$", "$"); } + private String buildDaylightAmendComment(Bookings booking, Date checkinDate, Date checkoutDate) { + String amendLine = String.format("AMEND C/I ON %s AND C/O ON %s VIA APP", + DateTimeUtil.getDateByFormat(checkinDate, "dd MMM", Locale.ENGLISH).toUpperCase(Locale.ENGLISH), + DateTimeUtil.getDateByFormat(checkoutDate, "dd MMM", Locale.ENGLISH).toUpperCase(Locale.ENGLISH)); + String existingComment = booking.getOperaComment(); + + if (StringUtils.isBlank(existingComment)) { + return amendLine; + } + + return existingComment.replace("\r\n", "\n").replace("\r", "\n") + .replace(" ", "\n").replace("$", "$").trim() + + "\n" + amendLine; + } + private String buildODExtendCommentContent(Bookings booking, Date originCheckoutDate, String booker) { if (booking == null || originCheckoutDate == null) { return null; @@ -6219,6 +6501,10 @@ public class BookingService { } public void insertCommentAndMiscChargeFromDaylight(BookingDto dto) { + insertCommentAndMiscChargeFromDaylight(dto, null); + } + + private void insertCommentAndMiscChargeFromDaylight(BookingDto dto, String previousMiscCharge) { String dlReservationId = dto.getDlReservationId(); String dlPropertyId = dto.getDlPropertyId(); @@ -6240,7 +6526,11 @@ public class BookingService { .compile("(?:\\$|$)\\s*([0-9]+(?:\\.[0-9]+)?)", Pattern.CASE_INSENSITIVE) .matcher(comment); if (miscChargeMatcher.find()) { - dto.setMiscCharge(miscChargeMatcher.group(1)); + String miscCharge = miscChargeMatcher.group(1); + if (!Objects.equals(previousMiscCharge, miscCharge)) { + dto.setMiscCharge(miscCharge); + updateDaylightCompRule(dto, miscCharge); + } } comment = comment.replaceAll("(?i)", "\n"); } @@ -6248,4 +6538,81 @@ public class BookingService { } } } + + private void updateDaylightCompRule(BookingDto dto, String miscCharge) { + if (StringUtils.isBlank(dto.getDlAccountId()) || dto.getCheckinDate() == null || dto.getCheckoutDate() == null) { + return; + } + + List daylightViewList = daylightViewDao.findBySectionPropertyBookingTypeRoomType( + dto.getSectionId(), dto.getPropertyId(), dto.getBookingTypeId(), dto.getRoomTypeId() + ); + if (CollectionUtils.isEmpty(daylightViewList)) { + return; + } + + CfgDaylightView daylightView = daylightViewList.stream() + .filter(view -> Objects.nonNull(view.getCompRuleCategory()) && Objects.equals(view.getCompRuleCategory(), 2)) + .findFirst() + .orElse(null); + if (Objects.isNull(daylightView)) { + return; + } + + if (Objects.isNull(daylightView.getCompRuleCategory())) { + return; + } + + dlService.updateDaylightCompRule( + dto.getDlAccountId(), + dto.getDlPropertyId(), + DateTimeUtil.getDateByFormat(dto.getCheckinDate(), "yyyy-MM-dd"), + DateTimeUtil.getDateByFormat(dto.getCheckoutDate(), "yyyy-MM-dd"), + daylightView, + miscCharge + ); + } + + private BaseApiResponse getEmailInfo(BookingsView bookView, String userId) { + BaseApiResponse response = new BaseApiResponse<>(); + if(bookView!=null) { + JSONArray dynList = !StringUtils.isEmpty(bookView.getSharer())&&!Strings.CS.equals("[]", bookView.getSharer()) + ? + JSONArray.parseArray(bookView.getSharer()) :new JSONArray(); + StringBuilder sharerString = new StringBuilder(); + if (!dynList.isEmpty()) { + for (Object o : dynList) { + Gson gson = new Gson(); + JSONObject shareObj = (JSONObject) o; + String sharePlayerId = shareObj.getString("playerId"); + String shareName = shareObj.getString("name"); + LogUtil.info(BookingService.class, + "[editBooking] sharePlayerId " + gson.toJson(sharePlayerId)); + LogUtil.info(BookingService.class, + "[editBooking] shareName " + gson.toJson(shareName)); + if (!StringUtils.isEmpty(sharePlayerId)) { + sharerString.append(sharePlayerId).append(" ").append(shareName).append(" "); + } else if (!StringUtils.isEmpty(shareName)) { + sharerString.append(shareName).append(" "); + } else { + continue; + } + } + } + bookView.setSharerName(sharerString.toString()); + String playerId = bookView.getPlayerId()!=null? bookView.getPlayerId():null; + BaseApiResponse webPlayerProfileByPlayerId = playerService.findWebPlayerProfileByPlayerId(playerId); + if(webPlayerProfileByPlayerId.isSuccess()) { + try { + PlayerProfileEntry profile = webPlayerProfileByPlayerId.getData(); + bookView.setMemberTier(profile.getTierName()); + bookView.setMemberLastName(profile.getName()); + } catch (Exception e) { + LogUtil.error(ApprovalService.class,String.valueOf(e.getMessage())); + } + } + } + response.setData(bookView); + return response; + } } diff --git a/src/main/java/com/mgm/mcrm/booking/services/NonMGMBookingService.java b/src/main/java/com/mgm/mcrm/booking/services/NonMGMBookingService.java index 95b4772..789babf 100644 --- a/src/main/java/com/mgm/mcrm/booking/services/NonMGMBookingService.java +++ b/src/main/java/com/mgm/mcrm/booking/services/NonMGMBookingService.java @@ -39,7 +39,7 @@ import com.mgm.mcrm.common.dtos.data_import.booking.OtherBookingImportRow; import com.mgm.mcrm.common.dtos.excel_model.ImportDefine; import com.mgm.mcrm.common.dtos.mgm_api.*; import com.mgm.mcrm.common.enums.EnumBookingStatus; -import com.mgm.mcrm.common.services.MGMApiService; +import com.mgm.mcrm.common.services.MGMEsbApiService; import com.mgm.mcrm.common.utils.DateTimeUtil; import com.mgm.mcrm.common.utils.ExcelUtils; import com.mgm.mcrm.common.utils.LogUtil; @@ -63,7 +63,7 @@ import java.util.*; @Service public class NonMGMBookingService { @Resource - private MGMApiService mgmApiService; + private MGMEsbApiService mgmEsbApiService; @Resource private NonMGMBookingDao nonMgmBookingDao; @Resource diff --git a/src/main/java/com/mgm/mcrm/booking/services/OperaDBBookingService.java b/src/main/java/com/mgm/mcrm/booking/services/OperaDBBookingService.java index 1ee16c1..3927281 100644 --- a/src/main/java/com/mgm/mcrm/booking/services/OperaDBBookingService.java +++ b/src/main/java/com/mgm/mcrm/booking/services/OperaDBBookingService.java @@ -170,6 +170,7 @@ public class OperaDBBookingService { booking.setProfieExpDate(operaBook.getProfileExpDate()); booking.setRoomNo(operaBook.getRoomNo()); booking.setQueue(!StringUtils.isEmpty(operaBook.getIsQueue())); + booking.setMiscCharge(operaBook.getMiscCharge()); bookings.add(booking); } return bookings; @@ -180,6 +181,8 @@ public class OperaDBBookingService { for(int i= 0 ;i getBookingsByPlayer(String playerId) throws Exception { List bookingList = new ArrayList(); try { - HttpResponseResult httpResult = mgmApiService.invokeWaitlistBooking(playerId); + HttpResponseResult httpResult = mgmEsbApiService.invokeWaitlistBooking(playerId); if(httpResult.getStatusCode()==200) { try { WaitlistBookingApiResult resultObj = JSONObject.parseObject(httpResult.getResponseContent(),WaitlistBookingApiResult.class,Feature.SupportNonPublicField,Feature.IgnoreNotMatch); @@ -132,9 +131,9 @@ public class WaitlistBookingService { } public List getWaitlistData(String bookIds) throws Exception { - List gamingList = new ArrayList(); + List gamingList = new ArrayList<>(); try { - HttpResponseResult httpResult = mgmApiService.invokeWaitlistData(bookIds, 0); + HttpResponseResult httpResult = mgmEsbApiService.invokeWaitlistData(bookIds, 1); if(httpResult.getStatusCode()==200) { try { WaitlistDataApiResult resultObj = JSONObject.parseObject(httpResult.getResponseContent(),WaitlistDataApiResult.class,Feature.SupportNonPublicField,Feature.IgnoreNotMatch); @@ -143,8 +142,7 @@ public class WaitlistBookingService { if(Strings.CS.equals("OK", resultCode)) { List recordList = resultObj.getRecords(); for(WaitlistDataRecord rec:recordList) { - GamingData gamingData = new GamingData(); - gamingData = this.convertWaitlistDataRecord(rec); + GamingData gamingData = this.convertWaitlistDataRecord(rec); if(gamingData.getBookId()!=null) { gamingList.add(gamingData); } @@ -171,11 +169,11 @@ public class WaitlistBookingService { public List syncWaitlistData(String bookIds) throws Exception { List gamingList = new ArrayList(); gamingList = getWaitlistData(bookIds); - if(gamingList.size()>0) { + if(!gamingList.isEmpty()) { for(GamingData gd:gamingList) { List getGaming = gamingDataDao.findByBookId(gd.getBookId()); Date newDate = DateTimeUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss"); - if(getGaming!=null&&getGaming.size()>0) { + if(getGaming!=null&& !getGaming.isEmpty()) { if(getGaming.size()>1) { gamingDataDao.deleteAll(getGaming); gd.setCreatedAt(newDate); @@ -403,12 +401,50 @@ public class WaitlistBookingService { email = SendEmail.sendApprovalExtendEmail(emailBook, originBookingView, email, userId, userName, bookingView, isOtherOverBook, emailAddress.getKeyValues()); if (email.getStatus() == 1) { originBooking.setApproveStatus("WA"); - bookingDao.save(originBooking); } emailDao.save(email); - } else { -//Send mgm booking email + } else if (StringUtils.equalsIgnoreCase(newBookingProperty.getCategory(), "DAYLIGHT")) { + // The selected row is the waitlist extension. Extend the real booking + // referenced by waitlistId with the requested checkout date. + BaseApiResponse findPlayerResult = playerService + .findPlayerProfileByPlayerIdAndUserId(originBooking.getPlayerId(), userId); + if (!findPlayerResult.isSuccess() || findPlayerResult.getData() == null) { + processResult.setSuccess(false); + processResult.setMessage("Player profile not found for booking id: " + id); + return processResult; + } + + BaseApiResponse> extendResult = bookingService + .extendDaylightWaitlistBooking(originBooking, findPlayerResult.getData(), userId, userName); + if (!extendResult.isSuccess()) { + processResult.setSuccess(false); + processResult.setMessage("Extend Daylight booking failed for booking id: " + id + + ". " + StringUtils.defaultString(extendResult.getMessage())); + return processResult; + } + + //Directly send email when submit Approval Routing for daylight + BookingsView originBookingView = bookingViewDao.findById( + originBooking.getWaitlistId()).orElse(null); + if (originBookingView == null) { + processResult.setSuccess(false); + processResult.setMessage("Original booking view not found for booking id: " + id); + return processResult; + } + originBookingView = this.getEmailInfo(originBookingView,originBookingView.getCreatedBy()); + Email email = new Email(); + + email.setBookId(originBooking.getId()); + email.setCreatedAt(creatdDate); + email.setCreatedBy(originBooking.getCreatedBy()); + + email = SendEmail.sendApprovalExtendEmail(originBooking, originBookingView,email,userId,userName,bookingView,isOtherOverBook,emailAddress.getKeyValues()); + if(email.getStatus()==1) { + emailDao.save(email); + } + } else { + //Send mgm booking email BookingsView originBookingView = bookingViewDao.findById(originBooking.getWaitlistId()).orElse(null); assert originBookingView != null; originBookingView = this.getEmailInfo(originBookingView, originBookingView.getCreatedBy()); @@ -429,7 +465,7 @@ public class WaitlistBookingService { emailDao.save(email); } } else { -//If overbook send email + //If overbook send email isOverBook = false; if ((isOverBook && originBooking.getBlockId() != null) || (isOtherOverBook && originBooking.getSegmentId() != null)) { Bookings emailBook = originBooking; @@ -499,9 +535,7 @@ public class WaitlistBookingService { email = SendEmail.sendApprovalNewEmail(emailBook, bookingView, email, userId, userName, false, true, emailAddress.getKeyValues()); emailDao.save(email); } - } - } }else if(Strings.CS.equals(status, "WR")||Strings.CS.equals(status, "WF")) { diff --git a/src/main/java/com/mgm/mcrm/common/config/ApiGatewayConfig.java b/src/main/java/com/mgm/mcrm/common/config/ApiGatewayConfig.java new file mode 100644 index 0000000..7cb0786 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/common/config/ApiGatewayConfig.java @@ -0,0 +1,125 @@ +package com.mgm.mcrm.common.config; + +import com.mgm.mcrm.common.dtos.config.ApiGatewaySetting; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ApiGatewayConfig { + + @Value("${api-gateway.base-url}") + private String serverUrl; + + @Value("${api-gateway.request-timeout-ms:60000}") + private int apiRequestTimeout; + + //-- Daylight + @Value("${api-gateway.daylight.proxy-path}") + private String daylightProxyPath; + + @Value("${api-gateway.daylight.app-id}") + private String daylightAppId; + + @Value("${api-gateway.daylight.auth-code}") + private String daylightAppSecret; + + @Value("${api-gateway.daylight.system-id}") + private String daylightSystemId; + + //-- Opera + @Value("${api-gateway.opera.proxy-path}") + private String operaProxyPath; + + @Value("${api-gateway.daylight.app-id}") + private String operaAppId; + + @Value("${api-gateway.daylight.auth-code}") + private String operaAppSecret; + + //-- ESB MCRM + @Value("${api-gateway.esb-mcrm.proxy-path}") + private String esbMcrmProxyPath; + + @Value("${api-gateway.esb-mcrm.app-id}") + private String esbMcrmAppId; + + @Value("${api-gateway.esb-mcrm.auth-code}") + private String esbMcrmAppSecret; + + //-- ESB Limo + @Value("${api-gateway.esb-limo.proxy-path}") + private String esbLimoProxyPath; + + @Value("${api-gateway.esb-limo.app-id}") + private String esbLimoAppId; + + @Value("${api-gateway.esb-limo.auth-code}") + private String esbLimoAppSecret; + + //-- Limo + @Value("${api-gateway.limo.proxy-path}") + private String limoProxyPath; + + @Value("${api-gateway.limo.app-id}") + private String limoAppId; + + @Value("${api-gateway.limo.auth-code}") + private String limoAppSecret; + + @Value("${api-gateway.limo.main-handle-url}") + private String limoMainHandleUrl; + + @Value("${api-gateway.limo.access-account}") + private String limoAccessAccount; + + @Value("${api-gateway.limo.access-password}") + private String limoAccessPassword; + + @Value("${api-gateway.limo.token-expiry-seconds:60}") + private int limoTokenExpirySeconds; + + //-- Notification + @Value("${api-gateway.notification.proxy-path}") + private String notificationProxyPath; + + @Value("${api-gateway.notification.app-id}") + private String notificationAppId; + + @Value("${api-gateway.notification.auth-code}") + private String notificationAppSecret; + + + @Bean + public ApiGatewaySetting configApiGateway() { + ApiGatewaySetting config = new ApiGatewaySetting(); + config.setBaseUrl(serverUrl); + config.setRequestTimeout(apiRequestTimeout); + config.setDaylightProxyUrl(serverUrl+daylightProxyPath); + config.setDaylightAppId(daylightAppId); + config.setDaylightAppSecret(daylightAppSecret); + config.setDaylightSystemId(daylightSystemId); + config.setOperaProxyUrl(serverUrl+operaProxyPath); + config.setOperaAppId(operaAppId); + config.setOperaAppSecret(operaAppSecret); + config.setEsbMcrmProxyUrl(serverUrl+esbMcrmProxyPath); + config.setEsbMcrmAppId(esbMcrmAppId); + config.setEsbMcrmAppSecret(esbMcrmAppSecret); + config.setEsbLimoProxyUrl(serverUrl+esbLimoProxyPath); + config.setEsbLimoAppId(esbLimoAppId); + config.setEsbLimoAppSecret(esbLimoAppSecret); + config.setLimoProxyUrl(serverUrl+limoProxyPath); + config.setLimoAppId(limoAppId); + config.setLimoAppSecret(limoAppSecret); + config.setLimoMainHandleUrl(limoMainHandleUrl); + config.setLimoAccessAccount(limoAccessAccount); + config.setLimoAccessPassword(limoAccessPassword); + config.setLimoTokenExpirySecond(limoTokenExpirySeconds); + config.setNotificationProxyUrl(serverUrl+notificationProxyPath); + config.setNotificationAppId(notificationAppId); + config.setNotificationAppSecret(notificationAppSecret); + //-- + return config; + } +} + diff --git a/src/main/java/com/mgm/mcrm/common/config/MGMDataSourceConfig.java b/src/main/java/com/mgm/mcrm/common/config/MGMDataSourceConfig.java index 9275b52..d04231f 100644 --- a/src/main/java/com/mgm/mcrm/common/config/MGMDataSourceConfig.java +++ b/src/main/java/com/mgm/mcrm/common/config/MGMDataSourceConfig.java @@ -4,11 +4,11 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings; -import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; +import org.springframework.boot.hibernate.autoconfigure.HibernateProperties; +import org.springframework.boot.hibernate.autoconfigure.HibernateSettings; +import org.springframework.boot.jpa.EntityManagerFactoryBuilder; +import org.springframework.boot.jpa.autoconfigure.JpaProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @@ -51,7 +51,7 @@ public class MGMDataSourceConfig { @Primary @Bean(name = "entityManagerFactory") //mrbs实体工厂 public LocalContainerEntityManagerFactoryBean entityManagerFactory (@Qualifier("mgmDataSource") DataSource mgmDataSource, - @Qualifier("mgmJpaProperties") JpaProperties jpaProperties,EntityManagerFactoryBuilder builder) { + @Qualifier("mgmJpaProperties") JpaProperties jpaProperties, EntityManagerFactoryBuilder builder) { return builder.dataSource(mgmDataSource) .properties(jpaProperties.getProperties()) .properties( diff --git a/src/main/java/com/mgm/mcrm/common/config/OperaDataSourceConfig.java b/src/main/java/com/mgm/mcrm/common/config/OperaDataSourceConfig.java index 22f52cf..93afba6 100644 --- a/src/main/java/com/mgm/mcrm/common/config/OperaDataSourceConfig.java +++ b/src/main/java/com/mgm/mcrm/common/config/OperaDataSourceConfig.java @@ -4,11 +4,11 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties; -import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings; -import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder; +import org.springframework.boot.hibernate.autoconfigure.HibernateProperties; +import org.springframework.boot.hibernate.autoconfigure.HibernateSettings; +import org.springframework.boot.jpa.EntityManagerFactoryBuilder; +import org.springframework.boot.jpa.autoconfigure.JpaProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @@ -46,7 +46,7 @@ public class OperaDataSourceConfig { @Bean(name = "entityManagerFactoryOpera") public LocalContainerEntityManagerFactoryBean entityManagerFactoryOpera (@Qualifier("operaDataSource") DataSource operaDataSource, - @Qualifier("operaJpaProperties") JpaProperties jpaProperties,EntityManagerFactoryBuilder builder) { + @Qualifier("operaJpaProperties") JpaProperties jpaProperties, EntityManagerFactoryBuilder builder) { return builder.dataSource(operaDataSource) .properties(jpaProperties.getProperties()) .properties( diff --git a/src/main/java/com/mgm/mcrm/common/constants/SysCommon.java b/src/main/java/com/mgm/mcrm/common/constants/SysCommon.java index a21e071..c4bf968 100644 --- a/src/main/java/com/mgm/mcrm/common/constants/SysCommon.java +++ b/src/main/java/com/mgm/mcrm/common/constants/SysCommon.java @@ -46,14 +46,13 @@ public class SysCommon { //for stress test public static int tokenExpMin = 5; public static int refreshTokenExpDay = 30; - public static String tokenSecret = "SecretKeyWhichShouldBeLongEnoughToBeSecure67848563"; public static APIConfig apiConfig = new APIConfig(); private static Map notifyTemplateMap = new HashMap(); - private static Map appServerStates = new HashMap(); - private static List allowOriginSource = new ArrayList(); - private static List soapServers = new ArrayList(); - private static List httpApiServers = new ArrayList(); - private static List allowIPSource = new ArrayList(); + private static final Map appServerStates = new HashMap(); + private static final List allowOriginSource = new ArrayList(); + private static final List soapServers = new ArrayList(); + private static final List httpApiServers = new ArrayList(); + private static final List allowIPSource = new ArrayList(); // @Autowired private static MessageSource messageSource; @@ -67,7 +66,7 @@ public class SysCommon { runtime_path = getSystemPath(); } - public static boolean loadSystemConfig() { + public static void loadSystemConfig() { boolean isLoaded = true; Properties props = new Properties(); try { @@ -87,7 +86,6 @@ public class SysCommon { props.clear(); props = null; } - return isLoaded; } public static void loadSettingsFromDB(List sysSettings) { @@ -384,42 +382,8 @@ public class SysCommon { } // Load API Config - public static boolean loadAPIConfig(Properties props) { + public static void loadAPIConfig(Properties props) { boolean isLoaded = true; - // ESB - SysCommon.apiConfig.setServerUrl_ESB(props.getProperty("esb_server", "")); - SysCommon.apiConfig.setApiKey_ESB(props.getProperty("esb_app_id", "")); - SysCommon.apiConfig.setApiSecret_ESB(props.getProperty("esb_app_secret", "")); - try { - SysCommon.apiConfig.setTimeout_ESB(Integer.parseInt(props.getProperty("esb_request_timeout", "30000"))); - } catch (Exception err) { - SysCommon.apiConfig.setTimeout_ESB(0); - } - // OWS - SysCommon.apiConfig.setServerUrl_OWS(props.getProperty("ows_server", "")); - try { - SysCommon.apiConfig.setTimeout_OWS(Integer.parseInt(props.getProperty("ows_request_timeout", "30000"))); - } catch (Exception err) { - SysCommon.apiConfig.setTimeout_OWS(0); - } - // RPA - SysCommon.apiConfig.setServerLoginUrl_RPA(props.getProperty("rpa_login_server", "")); - SysCommon.apiConfig.setApiAccessName_RPA(props.getProperty("rpa_username", "")); - SysCommon.apiConfig.setApiAccessPassword_RPA(props.getProperty("rpa_password", "")); - SysCommon.apiConfig.setApiAccessKey_RPA(props.getProperty("rpa_apiKey", "")); - SysCommon.apiConfig.setServerAddUrl_RPA(props.getProperty("rpa_add_server", "")); - SysCommon.apiConfig.setServerExtensionUrl_RPA(props.getProperty("rpa_extension_server", "")); - // SSO - SysCommon.apiConfig.setServerUrl_SSO(props.getProperty("idp_server", "")); - SysCommon.apiConfig.setClientId_SSO(props.getProperty("idp_client_id", "")); - SysCommon.apiConfig.setClientSecret_SSO(props.getProperty("idp_client_secret", "")); - //SMS Config - SysCommon.apiConfig.setTokenUrl_SMS(props.getProperty("sms_token_url", "")); - SysCommon.apiConfig.setSendUrl_SMS(props.getProperty("sms_send_url", "")); - SysCommon.apiConfig.setUsername_SMS(props.getProperty("sms_username", "")); - SysCommon.apiConfig.setPassword_SMS(props.getProperty("sms_password", "")); - SysCommon.apiConfig.setAccount_SMS(props.getProperty("sms_account", "")); - SysCommon.apiConfig.setPriority_SMS(props.getProperty("sms_priority", "")); //SMS Config SysCommon.apiConfig.setPort_SMTP(props.getProperty("mail_port", "")); SysCommon.apiConfig.setHost_SMTP(props.getProperty("mail_smtp_host", "")); @@ -428,7 +392,6 @@ public class SysCommon { SysCommon.apiConfig.setSend_to_SMTP(props.getProperty("mail_send_to", "")); LogUtil.info(SysCommon.class, "Loaded API config."); - return isLoaded; } //-- Path Type 1= Class path 2=Application path 3=URI Path diff --git a/src/main/java/com/mgm/mcrm/common/dtos/config/APIConfig.java b/src/main/java/com/mgm/mcrm/common/dtos/config/APIConfig.java index 8c221e7..6af10e6 100644 --- a/src/main/java/com/mgm/mcrm/common/dtos/config/APIConfig.java +++ b/src/main/java/com/mgm/mcrm/common/dtos/config/APIConfig.java @@ -6,53 +6,7 @@ import lombok.Data; public class APIConfig { private boolean isLoaded = false; - //-- ESB Server - private String serverUrl_ESB= ""; - private String apiKey_ESB=""; - private String apiSecret_ESB=""; - private int timeout_ESB=0; - - private String limoService_ESB=""; - private String limoApp_id=""; - private String limoApp_key=""; - - private String serverUrl_ESB_Daylight=""; - private String apiKey_ESB_Daylight=""; - private String apiSecret_ESB_Daylight=""; - private int timeout_ESB_Daylight=0; - - //-- OWS Server - private String serverUrl_OWS= ""; - private int timeout_OWS=0; - - //-- RPA Server - private String serverLoginUrl_RPA= ""; - private String apiAccessName_RPA=""; - private String apiAccessPassword_RPA=""; - private String apiAccessKey_RPA=""; - private String serverAddUrl_RPA= ""; - private String serverExtensionUrl_RPA= ""; - private int timeout_RPA=0; - - //-- SSO Server - private String serverUrl_SSO= ""; - private String clientId_SSO=""; - private String clientSecret_SSO=""; - private int timeout_SSO=0; - - //-- CallCenter Server - private String serverUrl_CallCenter= ""; - private int timeout_CallCenter=0; - - //SMS Config - private String tokenUrl_SMS= ""; - private String sendUrl_SMS= ""; - private String username_SMS= ""; - private String password_SMS= ""; - private String account_SMS= ""; - private String priority_SMS= ""; - //SMTP Config private String port_SMTP= ""; private String host_SMTP= ""; diff --git a/src/main/java/com/mgm/mcrm/common/dtos/config/ApiGatewaySetting.java b/src/main/java/com/mgm/mcrm/common/dtos/config/ApiGatewaySetting.java new file mode 100644 index 0000000..49e376d --- /dev/null +++ b/src/main/java/com/mgm/mcrm/common/dtos/config/ApiGatewaySetting.java @@ -0,0 +1,32 @@ +package com.mgm.mcrm.common.dtos.config; + +import lombok.Data; + +@Data +public class ApiGatewaySetting { + private String baseUrl; + private int requestTimeout=30000; + private String daylightProxyUrl; + private String daylightSystemId; + private String daylightAppId; + private String daylightAppSecret; + private String operaProxyUrl; + private String operaAppId; + private String operaAppSecret; + private String esbMcrmProxyUrl; + private String esbMcrmAppId; + private String esbMcrmAppSecret; + private String esbLimoProxyUrl; + private String esbLimoAppId; + private String esbLimoAppSecret; + private String limoProxyUrl; + private String limoAppId; + private String limoAppSecret; + private String limoMainHandleUrl; + private String limoAccessAccount; + private String limoAccessPassword; + private int limoTokenExpirySecond; + private String notificationProxyUrl; + private String notificationAppId; + private String notificationAppSecret; +} diff --git a/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/ApproverRecord.java b/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/ApproverRecord.java index e7f6215..211b793 100644 --- a/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/ApproverRecord.java +++ b/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/ApproverRecord.java @@ -1,25 +1,32 @@ package com.mgm.mcrm.common.dtos.mgm_api; +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonProperty; + public class ApproverRecord { - private String EmployeeId = null; - private String EmployeeName = null; + @JSONField(name = "EmployeeId") + @JsonProperty("employeeId") + private String employeeId = null; + @JSONField(name = "EmployeeName") + @JsonProperty("employeeName") + private String employeeName = null; public String getEmployeeId() { - return EmployeeId; + return employeeId; } public void setEmployeeId(String employeeId) { - EmployeeId = employeeId; + this.employeeId = employeeId; } public String getEmployeeName() { - return EmployeeName; + return employeeName; } public void setEmployeeName(String employeeName) { - EmployeeName = employeeName; + this.employeeName = employeeName; } } diff --git a/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/ApproverRecordResult.java b/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/ApproverRecordResult.java index 4fa5456..575f62e 100644 --- a/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/ApproverRecordResult.java +++ b/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/ApproverRecordResult.java @@ -2,6 +2,8 @@ package com.mgm.mcrm.common.dtos.mgm_api; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonProperty; import java.util.ArrayList; import java.util.List; @@ -9,73 +11,117 @@ import java.util.List; public class ApproverRecordResult { - private String PlayerId = null; - private String BookingType = null; - private String Property = null; - private String RoomCategory = null; - private String StartDate = null; - private String EndDate = null; - private String ApprovalRoutingType = null; - private String ApprovalRoutingMessage = null; - private Object ApproverList; + @JSONField(name = "PlayerId") + private String playerId = null; + @JSONField(name = "BookingType") + private String bookingType = null; + @JSONField(name = "Property") + private String property = null; + @JSONField(name = "RoomCategory") + private String roomCategory = null; + @JSONField(name = "StartDate") + private String startDate = null; + @JSONField(name = "EndDate") + private String endDate = null; + @JSONField(name = "ApprovalRoutingType") + private String approvalRoutingType = null; + @JSONField(name = "ApprovalRoutingMessage") + private String approvalRoutingMessage = null; + @JSONField(name = "ApproverList") + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private Object approverListRaw; public String getPlayerId() { - return PlayerId; + return playerId; } public void setPlayerId(String playerId) { - PlayerId = playerId; + this.playerId = playerId; } public String getBookingType() { - return BookingType; + return bookingType; } public void setBookingType(String bookingType) { - BookingType = bookingType; + this.bookingType = bookingType; } public String getProperty() { - return Property; + return property; } public void setProperty(String property) { - Property = property; + this.property = property; } public String getRoomCategory() { - return RoomCategory; + return roomCategory; } public void setRoomCategory(String roomCategory) { - RoomCategory = roomCategory; + this.roomCategory = roomCategory; + } + + public String getStartDate() { + return startDate; + } + + public void setStartDate(String startDate) { + this.startDate = startDate; + } + + public String getEndDate() { + return endDate; + } + + public void setEndDate(String endDate) { + this.endDate = endDate; } public String getApprovalRoutingType() { - return ApprovalRoutingType; + return approvalRoutingType; } public void setApprovalRoutingType(String approvalRoutingType) { - ApprovalRoutingType = approvalRoutingType; + this.approvalRoutingType = approvalRoutingType; } public String getApprovalRoutingMessage() { - return ApprovalRoutingMessage; + return approvalRoutingMessage; } public void setApprovalRoutingMessage(String approvalRoutingMessage) { - ApprovalRoutingMessage = approvalRoutingMessage; + this.approvalRoutingMessage = approvalRoutingMessage; } + @JSONField(name = "ApproverList") + public void setApproverList(Object approverList) { + this.approverListRaw = approverList; + } + + @JsonProperty("approverList") + @JSONField(serialize = false, deserialize = false) public List getApproverList() { - if (ApproverList == null) { + return parseApproverList(approverListRaw); + } + + private static List parseApproverList(Object raw) { + if (raw == null) { return new ArrayList(); } - if (ApproverList instanceof JSONArray arr) { + if (raw instanceof List list && !list.isEmpty() && list.getFirst() instanceof ApproverRecord) { + List result = new ArrayList(); + for (Object item : list) { + result.add((ApproverRecord) item); + } + return result; + } + if (raw instanceof JSONArray arr) { return arr.toJavaList(ApproverRecord.class); } - if (ApproverList instanceof String s) { + if (raw instanceof String s) { s = s.trim(); if (s.isEmpty()) { return new ArrayList(); @@ -83,20 +129,7 @@ public class ApproverRecordResult { JSONArray arr = JSONArray.parseArray(s); return arr == null ? new ArrayList() : arr.toJavaList(ApproverRecord.class); } - // List/Map 等其它情况:统一转成 JSON 再转回 List - JSONArray arr = JSON.parseArray(JSON.toJSONString(ApproverList)); + JSONArray arr = JSON.parseArray(JSON.toJSONString(raw)); return arr == null ? new ArrayList() : arr.toJavaList(ApproverRecord.class); } - - public void setApproverList(List approverList) { - ApproverList = approverList; - } - - public void setApproverList(String approverList) { - ApproverList = approverList; - } - - public void setApproverList(Object approverList) { - ApproverList = approverList; - } } diff --git a/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/ApproverResult.java b/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/ApproverResult.java index 1a95af6..e41e14a 100644 --- a/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/ApproverResult.java +++ b/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/ApproverResult.java @@ -1,5 +1,7 @@ package com.mgm.mcrm.common.dtos.mgm_api; +import com.alibaba.fastjson.annotation.JSONField; + import java.util.ArrayList; import java.util.List; @@ -7,31 +9,34 @@ import java.util.List; public class ApproverResult { - private String StatusCode = null; - private String StatusDescription=null; - private List Records = new ArrayList(); + @JSONField(name = "StatusCode") + private String statusCode = null; + @JSONField(name = "StatusDescription") + private String statusDescription=null; + @JSONField(name = "Records") + private List records = new ArrayList(); public String getStatusCode() { - return StatusCode; + return statusCode; } public void setStatusCode(String statusCode) { - StatusCode = statusCode; + this.statusCode = statusCode; } public String getStatusDescription() { - return StatusDescription; + return statusDescription; } public void setStatusDescription(String statusDescription) { - StatusDescription = statusDescription; + this.statusDescription = statusDescription; } public List getRecords() { - return Records; + return records; } public void setRecords(List records) { - Records = records; + this.records = records; } } diff --git a/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/TodoDateListRecord.java b/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/TodoDateListRecord.java index 46d6099..a7352ee 100644 --- a/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/TodoDateListRecord.java +++ b/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/TodoDateListRecord.java @@ -7,7 +7,7 @@ import java.util.List; public class TodoDateListRecord { - private String Date = null; + private String date = null; private List dateRecords = new ArrayList(); public TodoDateListRecord() { @@ -16,12 +16,12 @@ public class TodoDateListRecord { public String getDate() { - return Date; + return date; } public void setDate(String date) { - Date = date; + this.date = date; } diff --git a/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/TodoDateListResult.java b/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/TodoDateListResult.java index 5c64acf..db88e1f 100644 --- a/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/TodoDateListResult.java +++ b/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/TodoDateListResult.java @@ -9,7 +9,7 @@ public class TodoDateListResult { private String statusCode = null; private String statusDescription=null; - private List Records = new ArrayList(); + private List records = new ArrayList(); public TodoDateListResult() { @@ -32,11 +32,11 @@ public class TodoDateListResult { } public List getRecords() { - return Records; + return records; } public void setRecords(List records) { - Records = records; + this.records = records; } } diff --git a/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/TodoListRecord.java b/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/TodoListRecord.java index 2991a8b..1e35eed 100644 --- a/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/TodoListRecord.java +++ b/src/main/java/com/mgm/mcrm/common/dtos/mgm_api/TodoListRecord.java @@ -1,33 +1,61 @@ package com.mgm.mcrm.common.dtos.mgm_api; +import com.alibaba.fastjson.annotation.JSONField; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + import java.util.List; public class TodoListRecord { - private String Date = null; - private String ActionDate = null; - private String OperaConfirmationNo = null; - private Integer PlayerId = null; - private String TierName = null; - private String ChineseName = null; - private String EnglishName = null; - private String Salutation = null; - private Integer HasArrivalRecord = null; - private Integer HasDepartureRecord = null; - private Integer HasInhouseRecord = null; - private Integer HasOthersRecord = null; - private Integer HasLimoRecord = null; - private Integer HasFlightRecord = null; - private String ETA = null; + @JSONField(name = "Date") + private String date = null; + @JSONField(name = "ActionDate") + private String actionDate = null; + @JSONField(name = "OperaConfirmationNo") + private String operaConfirmationNo = null; + @JSONField(name = "PlayerId") + private Integer playerId = null; + @JSONField(name = "TierName") + private String tierName = null; + @JSONField(name = "ChineseName") + private String chineseName = null; + @JSONField(name = "EnglishName") + private String englishName = null; + @JSONField(name = "Salutation") + private String salutation = null; + @JSONField(name = "HasArrivalRecord") + private Integer hasArrivalRecord = null; + @JSONField(name = "HasDepartureRecord") + private Integer hasDepartureRecord = null; + @JSONField(name = "HasInhouseRecord") + private Integer hasInhouseRecord = null; + @JSONField(name = "HasOthersRecord") + private Integer hasOthersRecord = null; + @JSONField(name = "HasLimoRecord") + private Integer hasLimoRecord = null; + @JSONField(name = "HasFlightRecord") + private Integer hasFlightRecord = null; + @JSONField(name = "ETA") + @JsonProperty("eta") + private String eta = null; private Boolean roomReady = false; private Boolean isQueue = false; - private Integer BigWinFlag = null; - private Integer BigLossFlag = null; - private Integer NoPlayFlag = null; - private Integer MAId = null; - private String MA = null; - private List Descriptions = null; + @JSONField(name = "BigWinFlag") + private Integer bigWinFlag = null; + @JSONField(name = "BigLossFlag") + private Integer bigLossFlag = null; + @JSONField(name = "NoPlayFlag") + private Integer noPlayFlag = null; + @JSONField(name = "MAId") + @JsonProperty("maId") + private Integer maId = null; + @JSONField(name = "MA") + @JsonProperty("ma") + private String ma = null; + @JSONField(name = "Descriptions") + private List descriptions = null; public TodoListRecord() { @@ -36,135 +64,147 @@ public class TodoListRecord { public String getDate() { - return Date; + return date; } public void setDate(String date) { - Date = date; + this.date = date; } public String getActionDate() { - return ActionDate; + return actionDate; } public void setActionDate(String actionDate) { - ActionDate = actionDate; + this.actionDate = actionDate; } public String getOperaConfirmationNo() { - return OperaConfirmationNo; + return operaConfirmationNo; } public void setOperaConfirmationNo(String operaConfirmationNo) { - OperaConfirmationNo = operaConfirmationNo; + this.operaConfirmationNo = operaConfirmationNo; } public Integer getPlayerId() { - return PlayerId; + return playerId; } public void setPlayerId(Integer playerId) { - PlayerId = playerId; + this.playerId = playerId; } public String getTierName() { - return TierName; + return tierName; } public void setTierName(String tierName) { - TierName = tierName; + this.tierName = tierName; } public String getChineseName() { - return ChineseName; + return chineseName; } public void setChineseName(String chineseName) { - ChineseName = chineseName; + this.chineseName = chineseName; } public String getEnglishName() { - return EnglishName; + return englishName; } public void setEnglishName(String englishName) { - EnglishName = englishName; + this.englishName = englishName; } public String getSalutation() { - return Salutation; + return salutation; } public void setSalutation(String salutation) { - Salutation = salutation; + this.salutation = salutation; } public Integer getHasArrivalRecord() { - return HasArrivalRecord; + return hasArrivalRecord; } public void setHasArrivalRecord(Integer hasArrivalRecord) { - HasArrivalRecord = hasArrivalRecord; + this.hasArrivalRecord = hasArrivalRecord; } public Integer getHasDepartureRecord() { - return HasDepartureRecord; + return hasDepartureRecord; } public void setHasDepartureRecord(Integer hasDepartureRecord) { - HasDepartureRecord = hasDepartureRecord; + this.hasDepartureRecord = hasDepartureRecord; } public Integer getHasInhouseRecord() { - return HasInhouseRecord; + return hasInhouseRecord; } public void setHasInhouseRecord(Integer hasInhouseRecord) { - HasInhouseRecord = hasInhouseRecord; + this.hasInhouseRecord = hasInhouseRecord; } public Integer getHasOthersRecord() { - return HasOthersRecord; + return hasOthersRecord; } public void setHasOthersRecord(Integer hasOthersRecord) { - HasOthersRecord = hasOthersRecord; + this.hasOthersRecord = hasOthersRecord; } public Integer getHasLimoRecord() { - return HasLimoRecord; + return hasLimoRecord; } public void setHasLimoRecord(Integer hasLimoRecord) { - HasLimoRecord = hasLimoRecord; + this.hasLimoRecord = hasLimoRecord; } public Integer getHasFlightRecord() { - return HasFlightRecord; + return hasFlightRecord; } public void setHasFlightRecord(Integer hasFlightRecord) { - HasFlightRecord = hasFlightRecord; + this.hasFlightRecord = hasFlightRecord; } + @JsonProperty("eta") + public String getEta() { + return eta; + } + + @JsonProperty("eta") + public void setEta(String eta) { + this.eta = eta; + } + + @JsonIgnore public String getETA() { - return ETA; + return eta; } + @JsonIgnore public void setETA(String eTA) { - ETA = eTA; + this.eta = eTA; } public Boolean getRoomReady() { @@ -184,56 +224,71 @@ public class TodoListRecord { } public Integer getBigWinFlag() { - return BigWinFlag; + return bigWinFlag; } public void setBigWinFlag(Integer bigWinFlag) { - BigWinFlag = bigWinFlag; + this.bigWinFlag = bigWinFlag; } public Integer getBigLossFlag() { - return BigLossFlag; + return bigLossFlag; } public void setBigLossFlag(Integer bigLossFlag) { - BigLossFlag = bigLossFlag; + this.bigLossFlag = bigLossFlag; } public Integer getNoPlayFlag() { - return NoPlayFlag; + return noPlayFlag; } public void setNoPlayFlag(Integer noPlayFlag) { - NoPlayFlag = noPlayFlag; + this.noPlayFlag = noPlayFlag; } + @JsonProperty("maId") + public Integer getMaId() { + return maId; + } + + @JsonIgnore public Integer getMAId() { - return MAId; + return maId; } - @com.fasterxml.jackson.annotation.JsonProperty("maId") - public Integer getCalendarMaId() { - return MAId; + public void setMaId(Integer maId) { + this.maId = maId; } public void setMAId(Integer mAId) { - MAId = mAId; + this.maId = mAId; } + @JsonProperty("ma") + public String getMa() { + return ma; + } + + @JsonIgnore public String getMA() { - return MA; + return ma; + } + + public void setMa(String ma) { + this.ma = ma; } public void setMA(String mA) { - MA = mA; + this.ma = mA; } public List getDescriptions() { - return Descriptions; + return descriptions; } public void setDescriptions(List descriptions) { - Descriptions = descriptions; + this.descriptions = descriptions; } diff --git a/src/main/java/com/mgm/mcrm/common/filters/ConfigHeaderFilter.java b/src/main/java/com/mgm/mcrm/common/filters/ConfigHeaderFilter.java index 531e83c..9f10e6b 100644 --- a/src/main/java/com/mgm/mcrm/common/filters/ConfigHeaderFilter.java +++ b/src/main/java/com/mgm/mcrm/common/filters/ConfigHeaderFilter.java @@ -30,27 +30,23 @@ public class ConfigHeaderFilter implements Filter { if (response instanceof HttpServletResponse httpServletResponse) { String origin = ((HttpServletRequest) request).getHeader("Origin"); - if (StringUtils.isBlank(origin) && "127.0.0.1".equals(NetworkUtil.getClientIpAddr((HttpServletRequest) request))) { - httpServletResponse.setHeader("Access-Control-Allow-Origin", null); + String sanitizedOrigin = NetworkUtil.sanitizeCRLF(origin); + if (SysCommon.isAllowOriginSource(sanitizedOrigin) || "OPTIONS".equalsIgnoreCase(req.getMethod())) { + httpServletResponse.setHeader("Access-Control-Allow-Origin", sanitizedOrigin); } else { - String sanitizedOrigin = NetworkUtil.encodePathWithSpring(origin); - if (SysCommon.isAllowOriginSource(sanitizedOrigin)) { - httpServletResponse.setHeader("Access-Control-Allow-Origin", sanitizedOrigin); - } else { - LogUtil.warn(ConfigHeaderFilter.class, "Origin of [" + sanitizedOrigin + "] is not allowed"); - httpServletResponse.setHeader("Access-Control-Allow-Origin", ""); - log.warn("Blocked request: method={}, uri={}, query={}, origin={}, referer={}, ip={}, xff={}, userAgent={}", - req.getMethod(), - req.getRequestURI(), - req.getQueryString(), - req.getHeader("Origin"), - req.getHeader("Referer"), - req.getRemoteAddr(), - req.getHeader("X-Forwarded-For"), - req.getHeader("User-Agent")); + LogUtil.warn(ConfigHeaderFilter.class, "Origin of [" + sanitizedOrigin + "] is not allowed"); + httpServletResponse.setHeader("Access-Control-Allow-Origin", ""); + log.warn("Blocked request: method={}, uri={}, query={}, origin={}, referer={}, ip={}, xff={}, userAgent={}", + req.getMethod(), + req.getRequestURI(), + req.getQueryString(), + req.getHeader("Origin"), + req.getHeader("Referer"), + req.getRemoteAddr(), + req.getHeader("X-Forwarded-For"), + req.getHeader("User-Agent")); - return; - } + return; } httpServletResponse.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); httpServletResponse.addHeader("Access-Control-Allow-Headers", "Content-Type,Authorization,ClientId,Refreshtoken"); diff --git a/src/main/java/com/mgm/mcrm/common/schedule/BookingScheduleJob.java b/src/main/java/com/mgm/mcrm/common/schedule/BookingScheduleJob.java index 7eb6380..0958cea 100644 --- a/src/main/java/com/mgm/mcrm/common/schedule/BookingScheduleJob.java +++ b/src/main/java/com/mgm/mcrm/common/schedule/BookingScheduleJob.java @@ -60,7 +60,7 @@ public class BookingScheduleJob { ArrayList bookIds = new ArrayList(); for(Bookings b:bookings) { List getGaming = waitlistBookingService.findGamingDataByBookId(b.getId()); - if(getGaming==null||getGaming.size()==0) { + if(getGaming==null|| getGaming.isEmpty()) { bookIds.add(String.valueOf(b.getId())); } } diff --git a/src/main/java/com/mgm/mcrm/common/services/HttpService.java b/src/main/java/com/mgm/mcrm/common/services/HttpService.java index 625c220..3c87309 100644 --- a/src/main/java/com/mgm/mcrm/common/services/HttpService.java +++ b/src/main/java/com/mgm/mcrm/common/services/HttpService.java @@ -2,6 +2,7 @@ package com.mgm.mcrm.common.services; import com.alibaba.fastjson.JSON; import com.mgm.mcrm.common.dtos.HttpResult; +import com.mgm.mcrm.common.dtos.config.ApiGatewaySetting; import com.mgm.mcrm.common.enums.HttpMethods; import com.mgm.mcrm.common.httpclient.HttpClientExecutor; import com.mgm.mcrm.common.httpclient.HttpRequestOptions; @@ -10,12 +11,16 @@ import jakarta.annotation.Resource; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.message.BasicHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Map; /** @@ -37,14 +42,19 @@ public class HttpService { @Qualifier("httpRequestConfig") private RequestConfig requestConfig; - @Value("${daylight.url}") - private String daylightUrl; + @Resource + private ApiGatewaySetting apiGatewaySetting; - @Value("${notification_proxy.url}") - private String notificationProxyUrl; + private Header[] supplementDaylightProxySecurity(Header[] headers){ + List
newHeaders = (headers == null ? new ArrayList<>() : new ArrayList<>(Arrays.asList(headers))); + newHeaders.add(new BasicHeader("application-id",apiGatewaySetting.getDaylightAppId())); + newHeaders.add(new BasicHeader("Authorization",apiGatewaySetting.getDaylightAppSecret())); + return newHeaders.toArray(new Header[0]); + } public String sslGet(String url, Header[] headers, Map map) throws Exception { + String daylightUrl=apiGatewaySetting.getDaylightProxyUrl(); // todo 在這裏需要把請求頭的信息插入到數據庫裏 log.info("GET method, url={}, headers={}, map={}", daylightUrl + url, JSON.toJSONString(headers), JSON.toJSONString(map)); // 使用 HttpConfig 配置 GET 请求(带参数) @@ -55,7 +65,7 @@ public class HttpService { .encoding("utf-8") .client(httpClient); - String result = HttpClientExecutor.get(getConfig, headers); + String result = HttpClientExecutor.get(getConfig, supplementDaylightProxySecurity(headers)); log.info("GET request result size={}", result.length()); return result; @@ -63,6 +73,7 @@ public class HttpService { public HttpResult sslGetHttpResult(String url, Header[] headers, Map map) throws Exception { + String daylightUrl=apiGatewaySetting.getDaylightProxyUrl(); // todo 在這裏需要把請求頭的信息插入到數據庫裏 log.info("GET method, url={}, headers={}, map={}", daylightUrl + url, JSON.toJSONString(headers), JSON.toJSONString(map)); // 使用 HttpConfig 配置 GET 请求(带参数) @@ -73,8 +84,8 @@ public class HttpService { .encoding("utf-8") .client(httpClient); - HttpResult result = HttpClientExecutor.sendAndGetResp(getConfig, headers); - log.info("GET request result size={}", result.getResult().length()); + HttpResult result = HttpClientExecutor.sendAndGetResp(getConfig, supplementDaylightProxySecurity(headers)); + log.info("GET request result size={}, statusCode={}", result.getResult().length(), result.getStatusCode()); return result; } @@ -88,7 +99,9 @@ public class HttpService { * @return 接口結果的json數據 * @throws Exception 異常 */ + @Deprecated public String sslPut(String url, Header[] headers, Map map) throws Exception { + String daylightUrl=apiGatewaySetting.getDaylightProxyUrl(); log.info("PUT method, url={}, headers={}, map={}", daylightUrl + url, JSON.toJSONString(headers), JSON.toJSONString(map)); HttpRequestOptions putConfig = HttpRequestOptions.create() .url(daylightUrl + url) @@ -97,7 +110,7 @@ public class HttpService { .json(JSON.toJSONString(map)) .encoding("utf-8") .client(httpClient); - String result = HttpClientExecutor.send(putConfig, headers); + String result = HttpClientExecutor.send(putConfig, supplementDaylightProxySecurity(headers)); log.info("PUT request result={}", result); return result; @@ -112,7 +125,9 @@ public class HttpService { * @return 接口結果的json數據 * @throws Exception 異常 */ + @Deprecated public String sslDelete(String url, Header[] headers, Object requestBody) throws Exception { + String daylightUrl=apiGatewaySetting.getDaylightProxyUrl(); log.info("DELETE method, url={}, headers={}, map={}", daylightUrl + url, JSON.toJSONString(headers), JSON.toJSONString(requestBody)); HttpRequestOptions deleteConfig = HttpRequestOptions.create() .url(daylightUrl + url) @@ -120,7 +135,7 @@ public class HttpService { .json(JSON.toJSONString(requestBody)) .method(HttpMethods.DELETE) .client(httpClient); - String result = HttpClientExecutor.send(deleteConfig, headers); + String result = HttpClientExecutor.send(deleteConfig, supplementDaylightProxySecurity(headers)); log.info("DELETE request result={}", result); return result; } @@ -135,6 +150,7 @@ public class HttpService { * @throws Exception 異常 */ public HttpResult sslPost(String url, Header[] headers, Object requestBody) throws Exception { + String daylightUrl=apiGatewaySetting.getDaylightProxyUrl(); log.info("POST method, url={}, headers={}, map={}", daylightUrl + url, JSON.toJSONString(headers), JSON.toJSONString(requestBody)); HttpRequestOptions postConfig = HttpRequestOptions.create() .url(daylightUrl + url) @@ -142,9 +158,9 @@ public class HttpService { .json(JSON.toJSONString(requestBody)) .method(HttpMethods.POST) .client(httpClient); - HttpResult result = HttpClientExecutor.sendAndPostResp(postConfig, headers); + HttpResult result = HttpClientExecutor.sendAndPostResp(postConfig, supplementDaylightProxySecurity(headers)); // System.out.println("POST 请求结果:\n" + result); - log.info("POST request result size={}", result.getResult().length()); + log.info("POST request result size={}, statusCode={}", result.getResult() != null ? result.getResult().length() : null, result.getStatusCode()); return result; } @@ -158,6 +174,7 @@ public class HttpService { * @throws Exception 異常 */ public HttpResult sslPatch(String url, Header[] headers, Object requestBody) throws Exception { + String daylightUrl=apiGatewaySetting.getDaylightProxyUrl(); log.info("PATCH method, url={}, headers={}, map={}", daylightUrl + url, JSON.toJSONString(headers), JSON.toJSONString(requestBody)); HttpRequestOptions patchConfig = HttpRequestOptions.create() .url(daylightUrl + url) @@ -166,12 +183,20 @@ public class HttpService { .method(HttpMethods.PATCH) .encoding("utf-8") .client(httpClient); - HttpResult result = HttpClientExecutor.sendAndPatchResp(patchConfig, headers); - log.info("PATCH request result={}", result); + HttpResult result = HttpClientExecutor.sendAndPatchResp(patchConfig, supplementDaylightProxySecurity(headers)); + log.info("PATCH request result={}, statusCode={}", result.getResult(), result.getStatusCode()); return result; } + private Header[] supplementNotificationProxySecurity(Header[] headers){ + List
newHeaders = (headers == null ? new ArrayList<>() : new ArrayList<>(Arrays.asList(headers))); + newHeaders.add(new BasicHeader("application-id",apiGatewaySetting.getDaylightAppId())); + newHeaders.add(new BasicHeader("Authorization",apiGatewaySetting.getDaylightAppSecret())); + return newHeaders.toArray(new Header[0]); + } + public HttpResult sslSendNotification(String url, Header[] headers, SendNotificationRequest request) throws Exception { + String notificationProxyUrl=apiGatewaySetting.getNotificationProxyUrl(); log.info("POST method, send notification, url={}, headers={}, map={}", notificationProxyUrl + url, JSON.toJSONString(headers), JSON.toJSONString(request)); HttpRequestOptions postConfig = HttpRequestOptions.create() .url(notificationProxyUrl + url) @@ -179,7 +204,7 @@ public class HttpService { .json(JSON.toJSONString(request)) .method(HttpMethods.POST) .client(httpClient); - HttpResult result = HttpClientExecutor.sendAndPostResp(postConfig, headers); + HttpResult result = HttpClientExecutor.sendAndPostResp(postConfig, supplementNotificationProxySecurity(headers)); log.info("POST request result={}", result); return result; } diff --git a/src/main/java/com/mgm/mcrm/common/services/MGMApiService.java b/src/main/java/com/mgm/mcrm/common/services/MGMApiService.java deleted file mode 100644 index 3ab1a45..0000000 --- a/src/main/java/com/mgm/mcrm/common/services/MGMApiService.java +++ /dev/null @@ -1,468 +0,0 @@ -package com.mgm.mcrm.common.services; - -import com.alibaba.fastjson.JSONObject; -import com.google.gson.Gson; -import com.mgm.mcrm.booking.dtos.search.DateRange; -import com.mgm.mcrm.common.constants.SysCommon; -import com.mgm.mcrm.common.dtos.http_util.HttpRequestDefine; -import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; -import com.mgm.mcrm.common.utils.DateTimeUtil; -import com.mgm.mcrm.common.utils.HttpUtil; -import com.mgm.mcrm.common.utils.LogUtil; -import com.mgm.mcrm.common.utils.mockup.HttpUtilMockup; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Strings; -import org.springframework.stereotype.Service; - -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Service -public class MGMApiService { - public static String ESB_APP_ID = ""; - public static String ESB_APP_SECRET = ""; - public static String ESB_Server = ""; - public static String ESB_ACCESS_TOKEN = null; - public static Date ESB_EXPIRED_TIME = new Date(); - - private static final String API_GET_ACCESS_TOKEN = "/RequestAccessToken"; - private static final String API_GET_PLAYER_PROFILE = "/GetReportingLinePlayerInfo"; - private static final String API_CHECK_PLAYER_TPD = "/GetRoomBookingQualificationStatus"; - private static final String API_GET_USER_INFO = "/GetLoginUserInfo"; - private static final String API_NON_MGM_BOOKING = "/GetNonMGMRoomBookingRecord"; - private static final String API_WAITLIST_BOOKING = "/GetRoomBookingWaitList"; - private static final String API_CHECK_SECOND_ROOM_TPD = "/GetMultiRoomBookingQualificationStatus"; - private static final String API_CHECK_APPROVER = "/CheckApprover"; - private static final String API_WAITLIST_DATA = "/GetWaitlistData"; - private static final String API_GET_ALL_PLAYER_PROFILE = "/GetPlayerInfo"; - private static final String API_GET_TODO_DASHBOARD = "/GetDashboardData"; - private static final String API_GET_TODO_DETAIL = "/GetTodolistDetails"; - private static final String API_GET_APPROVER_DATA="/RequestWaitlistData"; - private static final String API_GET_APPROVER="/RequestRoomBookingApprovalRouting"; - private static final String API_GET_TODO_LIST = "/GetTodolist"; - private static final String API_REQUEST_ROOM_CALENDAR = "/RequestRoomCalendar"; - private static final String API_GET_LIMO_BOOKING_FROM_LEGACY = "/GetLimoBookingFromLegacy"; - - - public HttpResponseResult invokeRequestRoomCalendar( - String playerId, - String property, - String roomType, - String startDate, - String endDate) throws Exception { - return invokeRequestRoomCalendar(playerId, property, roomType, startDate, endDate, 0); - } - - public HttpResponseResult invokeRequestRoomCalendar( - String playerId, - String property, - String roomType, - String startDate, - String endDate, - int type) throws Exception { - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - requestHeader.put("Authorization", getAccessToken()); - if (StringUtils.isNotBlank(playerId)) { - requestObj.put("PlayerId", playerId); - } - requestObj.put("Property", property); - requestObj.put("RoomType", roomType); - requestObj.put("StartDate", startDate); - requestObj.put("EndDate", endDate); - return invokeESBAPI(ESB_Server, API_REQUEST_ROOM_CALENDAR, requestObj, "POST", requestHeader, type); - } - - public HttpResponseResult invokeGetPlayerProfile( - String playerId, - String userId) throws Exception { - HttpResponseResult result; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - requestHeader.put("PlayerId", playerId); - requestHeader.put("UserId", userId); - - requestObj.put("playerId", playerId); - requestObj.put("userId", userId); - result = this.invokeESBAPI(ESB_Server, API_GET_PLAYER_PROFILE, requestObj, "GET", requestHeader); - return result; - } - - public HttpResponseResult invokeGetAllPlayerProfile(String playerId) throws Exception { - HttpResponseResult result; - String apiLink = SysCommon.mgmApiMockup ? API_GET_PLAYER_PROFILE : API_GET_ALL_PLAYER_PROFILE; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - requestHeader.put("PlayerId", playerId); - - requestObj.put("playerId", playerId); - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "GET", requestHeader); - return result; - } - - public HttpResponseResult invokeCheckPlayerTPD( - String playerId, - String roomCate, - List dates, - String bookingType, - String property) throws Exception { - HttpResponseResult result; - String apiLink = API_CHECK_PLAYER_TPD; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - requestHeader.put("PlayerId", playerId); - requestHeader.put("RoomCategory", roomCate); - requestHeader.put("BookingType", bookingType); - requestHeader.put("Property", property); - requestHeader.put("StartDate", dates.getFirst()); - requestHeader.put("EndDate", dates.getLast()); - - requestObj.put("playerId", playerId); - requestObj.put("roomCategory", roomCate); - requestObj.put("startDate", dates.getFirst()); - requestObj.put("endDate", dates.getLast()); - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "GET", requestHeader, 1); - return result; - } - - public HttpResponseResult invokeGetApprover(String playerId,String bookingType,String property,String roomCategory,String checkInDate,String checkOutDate,String userId) throws Exception { - HttpResponseResult result = null; - String apiLink =API_GET_APPROVER; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - requestObj.put("PlayerId", playerId); - requestObj.put("BookingType", bookingType); - requestObj.put("Property", property); - requestObj.put("RoomCategory", roomCategory); - requestObj.put("StartDate", checkInDate); - requestObj.put("EndDate", checkOutDate); - requestObj.put("UserId", userId); - - - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "POST", requestHeader); - return result; - } - - public HttpResponseResult invokeGetApprovalData(String bookingId) throws Exception { - HttpResponseResult result = null; - String apiLink =API_GET_APPROVER_DATA; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - - requestObj.put("BookingId", bookingId); - - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "POST", requestHeader); - return result; - } - - public HttpResponseResult invokeCheckApprover( - String userId, - String userName, - String roomType, - String property) throws Exception { - HttpResponseResult result; - String apiLink = API_CHECK_APPROVER; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - - if (!StringUtils.isEmpty(userId)) { - requestObj.put("UserId", userId); - } - if (!StringUtils.isEmpty(userName)) { - requestObj.put("UserName", userName); - } - requestObj.put("RoomCategory", roomType); - requestObj.put("Property", property); - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "POST", requestHeader); - return result; - } - - public HttpResponseResult invokeGetUserInfo(String userId, String userName) throws Exception { - HttpResponseResult result; - String apiLink = API_GET_USER_INFO; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - if (!StringUtils.isEmpty(userId)) { - requestHeader.put("UserId", userId); - requestObj.put("userId", userId); - } - if (!StringUtils.isEmpty(userName)) { - requestHeader.put("UserName", userName); - requestObj.put("userName", userName); - } - - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "GET", requestHeader); - return result; - } - - public HttpResponseResult invokeNonMGMBooking(String playerId) throws Exception { - HttpResponseResult result; - String apiLink = API_NON_MGM_BOOKING; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - requestHeader.put("PlayerId", playerId); - - requestObj.put("playerId", playerId); - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "GET", requestHeader); - return result; - } - - public HttpResponseResult invokeWaitlistBooking(String playerId) throws Exception { - HttpResponseResult result; - String apiLink = API_WAITLIST_BOOKING; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - requestHeader.put("PlayerId", playerId); - - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "GET", requestHeader); - return result; - } - - public HttpResponseResult invokeWaitlistData(String bookIds, int categoryType) throws Exception { - HttpResponseResult result; - String apiLink = API_WAITLIST_DATA; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - - requestObj.put("BookingId", bookIds); - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "POST", requestHeader, categoryType); - return result; - } - - public HttpResponseResult invokeCheckPlayerSecondTPD( - String playerId, - String roomCate, - List dateRange, - String bookingType, - String property) throws Exception { - HttpResponseResult result; - String apiLink = API_CHECK_SECOND_ROOM_TPD; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - - requestObj.put("PlayerId", playerId); - requestObj.put("RoomCategory", roomCate); - requestObj.put("BookingType", bookingType); - requestObj.put("Property", property); - requestObj.put("DateRange", dateRange); - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "POST", requestHeader); - return result; - } - - public String getAccessToken() throws Exception { - String token = ESB_ACCESS_TOKEN; - if (SysCommon.mgmApiMockup) return token; - ESB_APP_ID = SysCommon.apiConfig.getApiKey_ESB(); - ESB_APP_SECRET = SysCommon.apiConfig.getApiSecret_ESB(); - ESB_Server = SysCommon.apiConfig.getServerUrl_ESB(); - if (token == null || ESB_EXPIRED_TIME.getTime() < new Date().getTime()) { - String apiLink = API_GET_ACCESS_TOKEN; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - requestObj.put("AppId", ESB_APP_ID); - requestObj.put("AppSecret", ESB_APP_SECRET); - HttpResponseResult result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "POST", requestHeader); - if (result.getStatusCode() == 200) { - JSONObject resultObj = JSONObject.parseObject(result.getResponseContent()); - String status = resultObj.getString("StatusCode"); - if (Strings.CS.equals("OK", status)) { - token = resultObj.getString("Token"); - ESB_EXPIRED_TIME = DateTimeUtil.getDateFromString(resultObj.getString("ExpireTime"), "yyyy-MM-dd HH:mm:ss"); - ESB_ACCESS_TOKEN = token; - } else { - token = null; - ESB_ACCESS_TOKEN = null; - ESB_EXPIRED_TIME = new Date(); - } - } - } - return token; - } - - public HttpResponseResult invokeGetTodoDashboard( - String userId, - int categoryType) throws Exception { - HttpResponseResult result; - String apiLink = API_GET_TODO_DASHBOARD; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - - requestObj.put("UserId", userId); - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "POST", requestHeader, categoryType); - return result; - } - - public HttpResponseResult invokeGetTodoDetail( - String playerId, - String todoCategory, - String todoSegment, - String date, - int categoryType) throws Exception { - HttpResponseResult result; - String apiLink = API_GET_TODO_DETAIL; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - - requestObj.put("PlayerId", playerId); - requestObj.put("TodoCategory", todoCategory); - requestObj.put("TodoSegment", todoSegment); - requestObj.put("Date", date); - - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "POST", requestHeader, categoryType); - return result; - } - - public HttpResponseResult invokeGetLimoBookingFromLegacy( - String playerId - ) throws Exception { - HttpResponseResult result = null; - String apiLink = API_GET_LIMO_BOOKING_FROM_LEGACY; - - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - - String token = getAccessToken(); - - requestHeader.put("Authorization", token); - - requestObj.put("PlayerId", playerId); - - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "POST", requestHeader); - return result; - } - - - public HttpResponseResult invokeGetTodoList( - String userId, - String category, - String type, - Date startDate, - Date endDate, - int categoryType) throws Exception { - HttpResponseResult result; - String apiLink = API_GET_TODO_LIST; - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - String token = getAccessToken(); - requestHeader.put("Authorization", token); - - requestObj.put("UserId", userId); - requestObj.put("TodoCategory", category); - requestObj.put("TodoSegment", type); - requestObj.put("StartDate", startDate); - requestObj.put("EndDate", endDate); - - result = this.invokeESBAPI(ESB_Server, apiLink, requestObj, "POST", requestHeader, categoryType); - return result; - } - - // 如果不设置type,默认call opera - public HttpResponseResult invokeESBAPI( - String serverType, - String requestLink, - JSONObject requestContent, - String requestMethod, - Map requestHeader) throws Exception { - return invokeESBAPI(serverType, requestLink, requestContent, requestMethod, requestHeader, 0); - } - - /** - * - * @param serverType n - * @param requestLink n - * @param requestContent n - * @param requestMethod n - * @param requestHeader n - * @param type 0 - opera, 1 - daylight - * @return n - * @throws Exception e - */ - public HttpResponseResult invokeESBAPI( - String serverType, - String requestLink, - JSONObject requestContent, - String requestMethod, - Map requestHeader, - int type) throws Exception { - String serverUrl; - int timeoutTime; - if (Strings.CS.equals(serverType, ESB_Server)) { - serverUrl = SysCommon.apiConfig.getServerUrl_ESB(); - timeoutTime = SysCommon.apiConfig.getTimeout_ESB(); - } else { - throw new Exception("Invalid server type"); - } - - if (type == 1) { - requestLink = "/Daylight" + requestLink; - } - - HttpRequestDefine requestDef = new HttpRequestDefine(); - String requestUrl = serverUrl + requestLink; - requestDef.setRequestUrl(requestUrl); - requestDef.setRequestMethod(requestMethod); - requestDef.setTimeout(timeoutTime); - requestHeader.put("Content-type", "application/json;charset=utf-8"); - requestDef.setHeaders(requestHeader); - - if (requestContent != null) { - requestDef.setEntityContent(requestContent.toString()); - } else { - requestDef.setEntityContent(""); - } - // -- - //Get Start,EndTime - long spanTime; - Date startTime = new Date(); - Date endTime; - LogUtil.info(MGMApiService.class, "[invokeESBAPI] start request:"); - HttpResponseResult httpResult = - SysCommon.mgmApiMockup ? - HttpUtilMockup.processHttpRequest(requestDef) : - HttpUtil.processHttpRequest(requestDef); - httpResult.setRequestContent(requestDef.getEntityContent()); - //RefreshEndTime - endTime = new Date(); - //Get Spend Time - Gson gson = new Gson(); - spanTime = DateTimeUtil.getTimeSpanSSS(startTime, endTime); - LogUtil.info(MGMApiService.class, "[invokeESBAPI]getRequestUrl:" + gson.toJson(requestDef.getRequestUrl())); - LogUtil.info(MGMApiService.class, "[invokeESBAPI]getHeaders:" + gson.toJson(requestDef.getHeaders())); - LogUtil.info(MGMApiService.class, "[invokeESBAPI]getRequestMethod:" + gson.toJson(requestDef.getRequestMethod())); - LogUtil.info(MGMApiService.class, "[invokeESBAPI]getEntityContent:" + gson.toJson(requestContent)); - LogUtil.info(MGMApiService.class, "[invokeESBAPI]getStatusCode:" + gson.toJson(httpResult.getStatusCode())); - LogUtil.info(MGMApiService.class, "[invokeESBAPI]getErrorMessage:" + gson.toJson(httpResult.getErrorMessage())); - LogUtil.info(MGMApiService.class, "[invokeESBAPI]getResponseContent:" + gson.toJson(httpResult.getResponseContent())); - //Log Spend Time - LogUtil.info(MGMApiService.class, "[invokeESBAPI]SpendTime:" + spanTime + "ms"); - return httpResult; - } -} diff --git a/src/main/java/com/mgm/mcrm/common/services/MGMEsbApiService.java b/src/main/java/com/mgm/mcrm/common/services/MGMEsbApiService.java index 1615916..eaddacc 100644 --- a/src/main/java/com/mgm/mcrm/common/services/MGMEsbApiService.java +++ b/src/main/java/com/mgm/mcrm/common/services/MGMEsbApiService.java @@ -2,244 +2,403 @@ package com.mgm.mcrm.common.services; import com.alibaba.fastjson.JSONObject; import com.google.gson.Gson; +import com.mgm.mcrm.booking.dtos.search.DateRange; import com.mgm.mcrm.common.constants.SysCommon; +import com.mgm.mcrm.common.dtos.config.ApiGatewaySetting; import com.mgm.mcrm.common.dtos.http_util.HttpRequestDefine; import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; import com.mgm.mcrm.common.utils.DateTimeUtil; import com.mgm.mcrm.common.utils.HttpUtil; -import com.mgm.mcrm.common.utils.LogUtil; import com.mgm.mcrm.common.utils.mockup.HttpUtilMockup; -import com.mgm.mcrm.limo.dtos.request.RequestPlayerEsbBookerRequest; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Strings; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; @Service public class MGMEsbApiService { - public static String LIMO_ESB_APP_ID = ""; - public static String LIMO_ESB_APP_SECRET = ""; - public static String LIMO_ESB_SERVER = ""; - public static String LIMO_ESB_ACCESS_TOKEN = ""; - public static Date LIMO_ESB_EXPIRED_TIME = new Date(); + private final Logger logger = LoggerFactory.getLogger(MGMEsbApiService.class); + public static final String ESB_MCRM_API_TYPE = "ESB_MCRM"; + public static final String ESB_LIMO_API_TYPE= "ESB_LIMO"; - private static final String API_GET_ACCESS_TOKEN = "/RequestAccessToken"; - private static final String API_GET_LIMO_OVERRIDER = "/RequestLimoOverrider"; - private static final String API_CHECK_SIMPLE_LIMO_BOOKING_QUALIFICATION_STATUS = "/RequestSimpleLimoBookingQualificationStatus"; - private static final String API_CHECK_COMPLEX_LIMO_BOOKING_QUALIFICATION_STATUS = "/RequestComplexLimoBookingQualificationStatus"; - private static final String API_REQUEST_EMPLOYEE_INFO = "/RequestEmployeeInfo"; - private static final String API_REQUEST_PLAYER_LIMO_BOOKER = "/RequestPlayerLimoBooker"; - private static final String API_REQUEST_PLAYER_INFO = "/RequestPlayerInfo"; + private static final String API_GET_PLAYER_PROFILE = "/GetReportingLinePlayerInfo"; + private static final String API_CHECK_PLAYER_TPD = "/GetRoomBookingQualificationStatus"; + private static final String API_GET_USER_INFO = "/GetLoginUserInfo"; + private static final String API_NON_MGM_BOOKING = "/GetNonMGMRoomBookingRecord"; + private static final String API_WAITLIST_BOOKING = "/GetRoomBookingWaitList"; + private static final String API_CHECK_SECOND_ROOM_TPD = "/GetMultiRoomBookingQualificationStatus"; + private static final String API_CHECK_APPROVER = "/CheckApprover"; + private static final String API_WAITLIST_DATA = "/GetWaitlistData"; + private static final String API_GET_ALL_PLAYER_PROFILE = "/GetPlayerInfo"; + private static final String API_GET_TODO_DASHBOARD = "/GetDashboardData"; + private static final String API_GET_TODO_DETAIL = "/GetTodolistDetails"; + private static final String API_GET_APPROVER_DATA="/RequestWaitlistData"; + private static final String API_GET_APPROVER="/RequestRoomBookingApprovalRouting"; + private static final String API_GET_TODO_LIST = "/GetTodolist"; + private static final String API_REQUEST_ROOM_CALENDAR = "/RequestRoomCalendar"; + private static final String API_GET_LIMO_BOOKING_FROM_LEGACY = "/GetLimoBookingFromLegacy"; + + @Autowired + private ApiGatewaySetting apiGatewaySetting; - public HttpResponseResult invokeRequestPlayerLimoBooker(RequestPlayerEsbBookerRequest request) throws Exception { - - String apiLink = API_REQUEST_PLAYER_LIMO_BOOKER; - - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - - String token = getLimoAccessToken(); - - requestHeader.put("Authorization", token); - - requestObj.put("EmployeeIdOrName", request.getUserId()); - requestObj.put("PlayerId", request.getPlayerId()); - - return this.invokeESBAPI(LIMO_ESB_SERVER, apiLink, requestObj, "POST", requestHeader); - + public HttpResponseResult invokeRequestRoomCalendar( + String playerId, + String property, + String roomType, + String startDate, + String endDate) throws Exception { + return invokeRequestRoomCalendar(playerId, property, roomType, startDate, endDate, 0); } - public String getLimoAccessToken() throws Exception { - - String token = LIMO_ESB_ACCESS_TOKEN; - - if (SysCommon.mgmApiMockup) - return token; - - LIMO_ESB_APP_ID = SysCommon.apiConfig.getLimoApp_id(); - LIMO_ESB_APP_SECRET = SysCommon.apiConfig.getLimoApp_key(); - LIMO_ESB_SERVER = SysCommon.apiConfig.getLimoService_ESB(); - - - if (token == null || LIMO_ESB_EXPIRED_TIME.getTime() < new Date().getTime()) { - - String apiLink = API_GET_ACCESS_TOKEN; - - JSONObject requestObj = new JSONObject(); - Map requestHeader = new HashMap<>(); - - requestObj.put("AppId", LIMO_ESB_APP_ID); - requestObj.put("AppSecret", LIMO_ESB_APP_SECRET); - - HttpResponseResult result = - this.invokeESBAPI(LIMO_ESB_SERVER, apiLink, requestObj, "POST", requestHeader); - - if (result.getStatusCode() == 200) { - JSONObject resultObj = JSONObject.parseObject(result.getResponseContent()); - String status = resultObj.getString("StatusCode"); - - if (Strings.CI.equals("OK", status)) { - token = resultObj.getString("Token"); - LIMO_ESB_EXPIRED_TIME = - DateTimeUtil.getDateFromString(resultObj.getString("ExpireTime"), "yyyy-MM-dd HH:mm:ss"); - LIMO_ESB_ACCESS_TOKEN = token; - } else { - token = null; - LIMO_ESB_ACCESS_TOKEN = null; - LIMO_ESB_EXPIRED_TIME = new Date(); - } - } + public HttpResponseResult invokeRequestRoomCalendar( + String playerId, + String property, + String roomType, + String startDate, + String endDate, + int type) throws Exception { + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + if (StringUtils.isNotBlank(playerId)) { + requestObj.put("PlayerId", playerId); } - - return token; + requestObj.put("Property", property); + requestObj.put("RoomType", roomType); + requestObj.put("StartDate", startDate); + requestObj.put("EndDate", endDate); + return invokeESBAPI(ESB_MCRM_API_TYPE, API_REQUEST_ROOM_CALENDAR, requestObj, "POST", requestHeader, type); } - public HttpResponseResult invokeLimoOverrider( - String overriderIdOrName - ) throws Exception { - - String apiLink = API_GET_LIMO_OVERRIDER; - + public HttpResponseResult invokeGetPlayerProfile( + String playerId, + String userId) throws Exception { + HttpResponseResult result; JSONObject requestObj = new JSONObject(); Map requestHeader = new HashMap<>(); + requestHeader.put("PlayerId", playerId); + requestHeader.put("UserId", userId); - String token = getLimoAccessToken(); - - requestHeader.put("Authorization", token); - - requestObj.put("OverriderIdOrName", "%"); - - return this.invokeESBAPI(LIMO_ESB_SERVER, apiLink, requestObj, "POST", requestHeader); + requestObj.put("playerId", playerId); + requestObj.put("userId", userId); + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_GET_PLAYER_PROFILE, requestObj, "GET", requestHeader); + return result; } - public HttpResponseResult invokeLimoPlayerInfo( - Integer playerId - ) throws Exception { - - String apiLink = API_REQUEST_PLAYER_INFO; - + public HttpResponseResult invokeGetAllPlayerProfile(String playerId) throws Exception { + HttpResponseResult result; + String apiLink = SysCommon.mgmApiMockup ? API_GET_PLAYER_PROFILE : API_GET_ALL_PLAYER_PROFILE; JSONObject requestObj = new JSONObject(); Map requestHeader = new HashMap<>(); + requestHeader.put("PlayerId", playerId); - String token = getLimoAccessToken(); - - requestHeader.put("Authorization", token); - - requestObj.put("PlayerId", playerId); - - return this.invokeESBAPI(LIMO_ESB_SERVER, apiLink, requestObj, "POST", requestHeader); + requestObj.put("playerId", playerId); + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, apiLink, requestObj, "GET", requestHeader); + return result; } - public HttpResponseResult invokeSimpleLimoBookingQualificationStatus( - Integer playerId, + public HttpResponseResult invokeCheckPlayerTPD( + String playerId, + String roomCate, + List dates, String bookingType, - String bookingDate - ) throws Exception { - - String apiLink = API_CHECK_SIMPLE_LIMO_BOOKING_QUALIFICATION_STATUS; - + String property) throws Exception { + HttpResponseResult result; JSONObject requestObj = new JSONObject(); Map requestHeader = new HashMap<>(); + requestHeader.put("PlayerId", playerId); + requestHeader.put("RoomCategory", roomCate); + requestHeader.put("BookingType", bookingType); + requestHeader.put("Property", property); + requestHeader.put("StartDate", dates.getFirst()); + requestHeader.put("EndDate", dates.getLast()); - String token = getLimoAccessToken(); - - requestHeader.put("Authorization", token); + requestObj.put("playerId", playerId); + requestObj.put("roomCategory", roomCate); + requestObj.put("startDate", dates.getFirst()); + requestObj.put("endDate", dates.getLast()); + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_CHECK_PLAYER_TPD, requestObj, "GET", requestHeader, 1); + return result; + } + public HttpResponseResult invokeGetApprover(String playerId,String bookingType,String property,String roomCategory,String checkInDate,String checkOutDate,String userId) throws Exception { + HttpResponseResult result = null; + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); requestObj.put("PlayerId", playerId); requestObj.put("BookingType", bookingType); - requestObj.put("BookingDate", bookingDate); + requestObj.put("Property", property); + requestObj.put("RoomCategory", roomCategory); + requestObj.put("StartDate", checkInDate); + requestObj.put("EndDate", checkOutDate); + requestObj.put("UserId", userId); - return this.invokeESBAPI(LIMO_ESB_SERVER, apiLink, requestObj, "POST", requestHeader); + + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_GET_APPROVER, requestObj, "POST", requestHeader, 1); + return result; + } + + public HttpResponseResult invokeGetApprovalData(String bookingId, int type) throws Exception { + HttpResponseResult result = null; + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap(); + + requestObj.put("BookingId", bookingId); + + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_GET_APPROVER_DATA, requestObj, "POST", requestHeader,type); + return result; } - public HttpResponseResult invokeRequestEmployeeInfo(String employeeId) throws Exception { - - String apiLink = API_REQUEST_EMPLOYEE_INFO; - + public HttpResponseResult invokeCheckApprover( + String userId, + String userName, + String roomType, + String property) throws Exception { + HttpResponseResult result; JSONObject requestObj = new JSONObject(); Map requestHeader = new HashMap<>(); - String token = getLimoAccessToken(); - - requestHeader.put("Authorization", token); - - requestObj.put("EmployeeId", employeeId); - - return this.invokeESBAPI(LIMO_ESB_SERVER, apiLink, requestObj, "POST", requestHeader); + if (!StringUtils.isEmpty(userId)) { + requestObj.put("UserId", userId); + } + if (!StringUtils.isEmpty(userName)) { + requestObj.put("UserName", userName); + } + requestObj.put("RoomCategory", roomType); + requestObj.put("Property", property); + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_CHECK_APPROVER, requestObj, "POST", requestHeader); + return result; } + public HttpResponseResult invokeGetUserInfo(String userId, String userName) throws Exception { + HttpResponseResult result; + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + if (!StringUtils.isEmpty(userId)) { + requestHeader.put("UserId", userId); + requestObj.put("userId", userId); + } + if (!StringUtils.isEmpty(userName)) { + requestHeader.put("UserName", userName); + requestObj.put("userName", userName); + } + + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_GET_USER_INFO, requestObj, "GET", requestHeader); + return result; + } + + public HttpResponseResult invokeNonMGMBooking(String playerId) throws Exception { + HttpResponseResult result; + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + requestHeader.put("PlayerId", playerId); + + requestObj.put("playerId", playerId); + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_NON_MGM_BOOKING, requestObj, "GET", requestHeader); + return result; + } + + public HttpResponseResult invokeWaitlistBooking(String playerId) throws Exception { + HttpResponseResult result; + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + requestHeader.put("PlayerId", playerId); + + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_WAITLIST_BOOKING, requestObj, "GET", requestHeader); + return result; + } + + public HttpResponseResult invokeWaitlistData(String bookIds, int categoryType) throws Exception { + HttpResponseResult result; + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + + requestObj.put("BookingId", bookIds); + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_WAITLIST_DATA, requestObj, "POST", requestHeader, categoryType); + return result; + } + + public HttpResponseResult invokeCheckPlayerSecondTPD( + String playerId, + String roomCate, + List dateRange, + String bookingType, + String property) throws Exception { + HttpResponseResult result; + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + + requestObj.put("PlayerId", playerId); + requestObj.put("RoomCategory", roomCate); + requestObj.put("BookingType", bookingType); + requestObj.put("Property", property); + requestObj.put("DateRange", dateRange); + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_CHECK_SECOND_ROOM_TPD, requestObj, "POST", requestHeader); + return result; + } + + public HttpResponseResult invokeGetTodoDashboard( + String userId, + int categoryType) throws Exception { + HttpResponseResult result; + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + requestObj.put("UserId", userId); + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_GET_TODO_DASHBOARD, requestObj, "POST", requestHeader, categoryType); + return result; + } + + public HttpResponseResult invokeGetTodoDetail( + String playerId, + String todoCategory, + String todoSegment, + String date, + int categoryType) throws Exception { + HttpResponseResult result; + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + + requestObj.put("PlayerId", playerId); + requestObj.put("TodoCategory", todoCategory); + requestObj.put("TodoSegment", todoSegment); + requestObj.put("Date", date); + + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_GET_TODO_DETAIL, requestObj, "POST", requestHeader, categoryType); + return result; + } + + public HttpResponseResult invokeGetLimoBookingFromLegacy( + String playerId + ) throws Exception { + HttpResponseResult result = null; + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + + requestObj.put("PlayerId", playerId); + + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_GET_LIMO_BOOKING_FROM_LEGACY, requestObj, "POST", requestHeader); + return result; + } + + + public HttpResponseResult invokeGetTodoList( + String userId, + String category, + String type, + Date startDate, + Date endDate, + int categoryType) throws Exception { + HttpResponseResult result; + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + + requestObj.put("UserId", userId); + requestObj.put("TodoCategory", category); + requestObj.put("TodoSegment", type); + requestObj.put("StartDate", startDate); + requestObj.put("EndDate", endDate); + + result = this.invokeESBAPI(ESB_MCRM_API_TYPE, API_GET_TODO_LIST, requestObj, "POST", requestHeader, categoryType); + return result; + } + + // 如果不设置type,默认call opera public HttpResponseResult invokeESBAPI( - String serverType, + String esbApiType, String requestLink, JSONObject requestContent, String requestMethod, Map requestHeader) throws Exception { + return invokeESBAPI(esbApiType, requestLink, requestContent, requestMethod, requestHeader, 0); + } + /** + * + * @param esbApiType n + * @param requestLink n + * @param requestContent n + * @param requestMethod n + * @param requestHeader n + * @param type 0 - opera, 1 - daylight + * @return n + * @throws Exception e + */ + public HttpResponseResult invokeESBAPI( + String esbApiType, + String requestLink, + JSONObject requestContent, + String requestMethod, + Map requestHeader, + int type) throws Exception { String serverUrl; - int timeoutTime; + int timeoutTime = apiGatewaySetting.getRequestTimeout(); + if (Strings.CS.equals(esbApiType, ESB_MCRM_API_TYPE)) { + serverUrl = apiGatewaySetting.getEsbMcrmProxyUrl(); + //-- set proxy authorization + requestHeader.put("application-id", apiGatewaySetting.getEsbMcrmAppId()); + requestHeader.put("Authorization", apiGatewaySetting.getEsbMcrmAppSecret()); + //-- - //Get Spend Time - Gson gson = new Gson(); - - HttpRequestDefine requestDef = new HttpRequestDefine(); - - LogUtil.info(MGMEsbApiService.class,"Request Url = "+gson.toJson(requestDef.getRequestUrl())+"serverType: " + serverType+"LIMO_ESB_SERVER"+LIMO_ESB_SERVER); - - if (Strings.CS.equals(serverType, LIMO_ESB_SERVER)) { - serverUrl = SysCommon.apiConfig.getLimoService_ESB(); - timeoutTime = SysCommon.apiConfig.getTimeout_ESB(); + if (type == 1) { + requestLink = "/Daylight" + requestLink; + } + }else if (Strings.CS.equals(esbApiType, ESB_LIMO_API_TYPE)) { + serverUrl = apiGatewaySetting.getEsbLimoProxyUrl(); + //-- set proxy authorization + requestHeader.put("application-id", apiGatewaySetting.getEsbLimoAppId()); + requestHeader.put("Authorization", apiGatewaySetting.getEsbLimoAppSecret()); + //-- } else { - throw new Exception("Invalid server type"); + throw new Exception("Invalid ESB Api type"); } + HttpRequestDefine requestDef = new HttpRequestDefine(); String requestUrl = serverUrl + requestLink; - requestDef.setRequestUrl(requestUrl); requestDef.setRequestMethod(requestMethod); requestDef.setTimeout(timeoutTime); - requestHeader.put("Content-type", "application/json;charset=utf-8"); - requestDef.setHeaders(requestHeader); - if (requestContent != null) + if (requestContent != null) { requestDef.setEntityContent(requestContent.toString()); - else + } else { requestDef.setEntityContent(""); - + } + // -- //Get Start,EndTime long spanTime; Date startTime = new Date(); - - LogUtil.info(MGMEsbApiService.class, "[invokeESBAPI] start request:"); - - HttpResponseResult httpResult = (SysCommon.mgmApiMockup ? - HttpUtilMockup.processHttpRequest(requestDef) - : HttpUtil.processHttpRequest(requestDef)); - + Date endTime; + logger.info("[invoke {} API] start request:",esbApiType); + HttpResponseResult httpResult = + SysCommon.mgmApiMockup ? + HttpUtilMockup.processHttpRequest(requestDef) : + HttpUtil.processHttpRequest(requestDef); httpResult.setRequestContent(requestDef.getEntityContent()); - //RefreshEndTime - Date endTime = new Date(); - + endTime = new Date(); + //Get Spend Time + Gson gson = new Gson(); spanTime = DateTimeUtil.getTimeSpanSSS(startTime, endTime); - - LogUtil.info(MGMEsbApiService.class, "[invokeLimoESBAPI]getRequestUrl:" + gson.toJson(requestDef.getRequestUrl())); - LogUtil.info(MGMEsbApiService.class, "[invokeLimoESBAPI]getHeaders:" + gson.toJson(requestDef.getHeaders())); - LogUtil.info(MGMEsbApiService.class, "[invokeLimoESBAPI]getRequestMethod:" + gson.toJson(requestDef.getRequestMethod())); - LogUtil.info(MGMEsbApiService.class, "[invokeLimoESBAPI]getEntityContent:" + gson.toJson(requestContent)); - LogUtil.info(MGMEsbApiService.class, "[invokeLimoESBAPI]getStatusCode:" + gson.toJson(httpResult.getStatusCode())); - LogUtil.info(MGMEsbApiService.class, "[invokeLimoESBAPI]getErrorMessage:" + gson.toJson(httpResult.getErrorMessage())); - LogUtil.info(MGMEsbApiService.class, "[invokeLimoESBAPI]getResponseContent:" + gson.toJson(httpResult.getResponseContent())); - + logger.info("[invoke {} API]getRequestUrl:{}",esbApiType, gson.toJson(requestDef.getRequestUrl())); + logger.info("[invoke {} API]getHeaders:{}" ,esbApiType, gson.toJson(requestDef.getHeaders())); + logger.info("[invoke {} API]getRequestMethod:{}" ,esbApiType, gson.toJson(requestDef.getRequestMethod())); + logger.info("[invoke {} API]getEntityContent:{}" ,esbApiType, gson.toJson(requestContent)); + logger.info("[invoke {} API]getStatusCode:{}" ,esbApiType, gson.toJson(httpResult.getStatusCode())); + logger.info("[invoke {} API]getErrorMessage:{}" ,esbApiType, gson.toJson(httpResult.getErrorMessage())); + logger.info("[invoke {} API]getResponseContent:{}" ,esbApiType, gson.toJson(httpResult.getResponseContent())); //Log Spend Time - LogUtil.info(MGMEsbApiService.class, "[invokeLimoESBAPI]SpendTime:" + spanTime + "ms"); - + logger.info("[invoke {} API]SpendTime:{}" ,esbApiType, spanTime + "ms"); return httpResult; } -} \ No newline at end of file +} diff --git a/src/main/java/com/mgm/mcrm/common/services/MGMEsbLimoApiService.java b/src/main/java/com/mgm/mcrm/common/services/MGMEsbLimoApiService.java new file mode 100644 index 0000000..e64f15b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/common/services/MGMEsbLimoApiService.java @@ -0,0 +1,91 @@ +package com.mgm.mcrm.common.services; + +import com.alibaba.fastjson.JSONObject; +import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; +import com.mgm.mcrm.limo.dtos.request.RequestPlayerEsbBookerRequest; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +@Service +public class MGMEsbLimoApiService { + + public static final String ESB_LIMO_API_TYPE= "ESB_LIMO"; + private static final String API_GET_ACCESS_TOKEN = "/RequestAccessToken"; + private static final String API_GET_LIMO_OVERRIDER = "/RequestLimoOverrider"; + private static final String API_CHECK_SIMPLE_LIMO_BOOKING_QUALIFICATION_STATUS = "/RequestSimpleLimoBookingQualificationStatus"; + private static final String API_CHECK_COMPLEX_LIMO_BOOKING_QUALIFICATION_STATUS = "/RequestComplexLimoBookingQualificationStatus"; + private static final String API_REQUEST_EMPLOYEE_INFO = "/RequestEmployeeInfo"; + private static final String API_REQUEST_PLAYER_LIMO_BOOKER = "/RequestPlayerLimoBooker"; + private static final String API_REQUEST_PLAYER_INFO = "/RequestPlayerInfo"; + + @Resource + private MGMEsbApiService mgmEsbApiService; + + + public HttpResponseResult invokeRequestPlayerLimoBooker(RequestPlayerEsbBookerRequest request) throws Exception { + + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + requestObj.put("EmployeeIdOrName", request.getUserId()); + requestObj.put("PlayerId", request.getPlayerId()); + + return mgmEsbApiService.invokeESBAPI(ESB_LIMO_API_TYPE, API_REQUEST_PLAYER_LIMO_BOOKER, requestObj, "POST", requestHeader); + + } + + + public HttpResponseResult invokeLimoOverrider( + String overriderIdOrName + ) throws Exception { + + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + + requestObj.put("OverriderIdOrName", "%"); + + return mgmEsbApiService.invokeESBAPI(ESB_LIMO_API_TYPE, API_GET_LIMO_OVERRIDER, requestObj, "POST", requestHeader); + } + + public HttpResponseResult invokeLimoPlayerInfo( + Integer playerId + ) throws Exception { + + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + + requestObj.put("PlayerId", playerId); + + return mgmEsbApiService.invokeESBAPI(ESB_LIMO_API_TYPE, API_REQUEST_PLAYER_INFO, requestObj, "POST", requestHeader); + } + + public HttpResponseResult invokeSimpleLimoBookingQualificationStatus( + Integer playerId, + String bookingType, + String bookingDate + ) throws Exception { + + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + + requestObj.put("PlayerId", playerId); + requestObj.put("BookingType", bookingType); + requestObj.put("BookingDate", bookingDate); + + return mgmEsbApiService.invokeESBAPI(ESB_LIMO_API_TYPE, API_CHECK_SIMPLE_LIMO_BOOKING_QUALIFICATION_STATUS, requestObj, "POST", requestHeader); + } + + + public HttpResponseResult invokeRequestEmployeeInfo(String employeeId) throws Exception { + + JSONObject requestObj = new JSONObject(); + Map requestHeader = new HashMap<>(); + + requestObj.put("EmployeeId", employeeId); + + return mgmEsbApiService.invokeESBAPI(ESB_LIMO_API_TYPE, API_REQUEST_EMPLOYEE_INFO, requestObj, "POST", requestHeader); + } + +} \ No newline at end of file diff --git a/src/main/java/com/mgm/mcrm/common/services/MGMLimoApiService.java b/src/main/java/com/mgm/mcrm/common/services/MGMLimoApiService.java index f3fd79c..cc67f5d 100644 --- a/src/main/java/com/mgm/mcrm/common/services/MGMLimoApiService.java +++ b/src/main/java/com/mgm/mcrm/common/services/MGMLimoApiService.java @@ -2,6 +2,7 @@ package com.mgm.mcrm.common.services; import com.alibaba.fastjson.JSONObject; import com.mgm.mcrm.common.constants.SysCommon; +import com.mgm.mcrm.common.dtos.config.ApiGatewaySetting; import com.mgm.mcrm.common.dtos.http_util.HttpRequestDefine; import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; import com.mgm.mcrm.common.utils.HttpUtil; @@ -10,11 +11,13 @@ import com.mgm.mcrm.common.utils.mockup.HttpUtilMockup; import com.mgm.mcrm.limo.dtos.request.*; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Strings; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.stream.Collectors; @@ -22,11 +25,8 @@ import java.util.stream.Collectors; @Service public class MGMLimoApiService { - public static String ESB_LIMO_SERVER = ""; - public static String ESB_LIMO_ACCESS_TOKEN = null; - public static String ESB_LIMO_APP_ID; - public static String ESB_LIMO_APP_SECRET; - public static Date ESB_LIMO_EXPIRED_TIME = new Date(); + public static String LIMO_ACCESS_TOKEN = null; + public static LocalDateTime LIMO_EXPIRED_TIME = LocalDateTime.now(); private static final String API_GET_ACCESS_TOKEN = "GetAccessToken"; private static final String API_GET_TRIP_PRICE = "GetTripPrice"; @@ -42,13 +42,8 @@ public class MGMLimoApiService { // private static final String API_MAIN_HANDLER_HANDLER = "http://mmlimodevapp01/MCRMAPI/API/MainHandler.ashx "; - - @Value("${limo_address}") - private String API_MAIN_HANDLER; - @Value("${limo_getAccessToken_Id}") - private String apiGetAccessTokenId; - @Value("${limo_getAccessToken_pwd}") - private String apiGetAccessTokenPwd; + @Autowired + private ApiGatewaySetting apiGatewaySetting; public HttpResponseResult invokeGetTripPrice() throws Exception { @@ -56,11 +51,10 @@ public class MGMLimoApiService { Map requestObj = new HashMap<>(); String token = getLimoAccessToken(); - requestObj.put("action", API_GET_TRIP_PRICE); requestObj.put("token", token); - return this.invokeESBFormUrlEncodedAPI(ESB_LIMO_SERVER, API_MAIN_HANDLER, requestObj, "POST", null); + return this.invokeESBFormUrlEncodedAPI(apiGatewaySetting.getLimoProxyUrl(), apiGatewaySetting.getLimoMainHandleUrl(), requestObj, "POST", null); } public HttpResponseResult invokeGetLocation(String location) throws Exception { @@ -68,12 +62,11 @@ public class MGMLimoApiService { Map requestObj = new HashMap<>(); String token = getLimoAccessToken(); - requestObj.put("action", API_GET_LOCATION); requestObj.put("token", token); requestObj.put("Location", "%"); - return this.invokeESBFormUrlEncodedAPI(ESB_LIMO_SERVER, API_MAIN_HANDLER, requestObj, "POST", null); + return this.invokeESBFormUrlEncodedAPI(apiGatewaySetting.getLimoProxyUrl(), apiGatewaySetting.getLimoMainHandleUrl(), requestObj, "POST", null); } public HttpResponseResult invokeGetCarType() throws Exception { @@ -85,7 +78,7 @@ public class MGMLimoApiService { requestObj.put("action", API_GET_CAR_TYPE); requestObj.put("token", token); - return this.invokeESBFormUrlEncodedAPI(ESB_LIMO_SERVER, API_MAIN_HANDLER, requestObj, "POST", null); + return this.invokeESBFormUrlEncodedAPI(apiGatewaySetting.getLimoProxyUrl(), apiGatewaySetting.getLimoMainHandleUrl(), requestObj, "POST", null); } @@ -99,7 +92,7 @@ public class MGMLimoApiService { requestObj.put("action", API_GET_GREET_TYPE); requestObj.put("token", token); - return this.invokeESBFormUrlEncodedAPI(ESB_LIMO_SERVER, API_MAIN_HANDLER, requestObj, "POST", null); + return this.invokeESBFormUrlEncodedAPI(apiGatewaySetting.getLimoProxyUrl(), apiGatewaySetting.getLimoMainHandleUrl(), requestObj, "POST", null); } public HttpResponseResult invokeGetBooking(String bookingId) throws Exception { @@ -107,13 +100,11 @@ public class MGMLimoApiService { Map requestObj = new HashMap<>(); String token = getLimoAccessToken(); - if(bookingId != null) requestObj.put("Id",bookingId); - requestObj.put("action", API_GET_BOOKING); requestObj.put("token", token); - return this.invokeESBFormUrlEncodedAPI(ESB_LIMO_SERVER, API_MAIN_HANDLER, requestObj, "POST", null); + return this.invokeESBFormUrlEncodedAPI(apiGatewaySetting.getLimoProxyUrl(), apiGatewaySetting.getLimoMainHandleUrl(), requestObj, "POST", null); } public HttpResponseResult getFlightDetails(GetLimoFlightDetailsRequest request) throws Exception { @@ -131,7 +122,7 @@ public class MGMLimoApiService { requestObj.put("token", token); requestObj.put("itemObject", itemObject.toJSONString()); - return this.invokeESBFormUrlEncodedAPI(ESB_LIMO_SERVER, API_MAIN_HANDLER, requestObj, "POST", null); + return this.invokeESBFormUrlEncodedAPI(apiGatewaySetting.getLimoProxyUrl(), apiGatewaySetting.getLimoMainHandleUrl(), requestObj, "POST", null); } public HttpResponseResult getTotalAmount(GetLimoTotalAmountRequest request) throws Exception { @@ -159,7 +150,7 @@ public class MGMLimoApiService { requestObj.put("token", token); requestObj.put("itemObject", itemObject.toJSONString()); - return this.invokeESBFormUrlEncodedAPI(ESB_LIMO_SERVER, API_MAIN_HANDLER, requestObj, "POST", null); + return this.invokeESBFormUrlEncodedAPI(apiGatewaySetting.getLimoProxyUrl(), apiGatewaySetting.getLimoMainHandleUrl(), requestObj, "POST", null); } /** 按 {@code ;} 拆分,去掉空白段;例如 {@code 1;2;3} → [1,2,3] */ @@ -293,7 +284,7 @@ public class MGMLimoApiService { requestObj.put("itemObject", sb.toString()); requestObj.put("token", token); - return this.invokeESBFormUrlEncodedAPI(ESB_LIMO_SERVER, API_MAIN_HANDLER, requestObj, "POST", null); + return this.invokeESBFormUrlEncodedAPI(apiGatewaySetting.getLimoProxyUrl(), apiGatewaySetting.getLimoMainHandleUrl(), requestObj, "POST", null); } // ---- Limo format helpers: single-quote + PascalCase + 1/0 for booleans ---- @@ -443,7 +434,7 @@ public class MGMLimoApiService { requestObj.put("itemObject", sb.toString()); requestObj.put("token", token); - return this.invokeESBFormUrlEncodedAPI(ESB_LIMO_SERVER, API_MAIN_HANDLER, requestObj, "POST", null); + return this.invokeESBFormUrlEncodedAPI(apiGatewaySetting.getLimoProxyUrl(), apiGatewaySetting.getLimoMainHandleUrl(), requestObj, "POST", null); } private static void appendStringUpdate(StringBuilder sb, String fieldName, String value) { @@ -499,35 +490,28 @@ public class MGMLimoApiService { requestObj.put("action", API_UPDATE_BOOKING_STATUS); - return this.invokeESBFormUrlEncodedAPI(ESB_LIMO_SERVER, API_MAIN_HANDLER, requestObj, "POST", null); + return this.invokeESBFormUrlEncodedAPI(apiGatewaySetting.getLimoProxyUrl(), apiGatewaySetting.getLimoMainHandleUrl(), requestObj, "POST", null); } private String getLimoAccessToken() throws Exception { - String token = ESB_LIMO_ACCESS_TOKEN; - + String token = LIMO_ACCESS_TOKEN; if (SysCommon.mgmApiMockup) return token; - - ESB_LIMO_APP_ID = SysCommon.apiConfig.getApiKey_ESB(); - ESB_LIMO_APP_SECRET = SysCommon.apiConfig.getApiSecret_ESB(); - ESB_LIMO_SERVER = SysCommon.apiConfig.getServerUrl_ESB(); - - if (token == null || ESB_LIMO_EXPIRED_TIME.getTime() < new Date().getTime()) { - + if (token == null || LIMO_EXPIRED_TIME.isBefore(LocalDateTime.now())) { String itemObject = String.format( "{'LoginID':'%s','Password':'%s'}", - apiGetAccessTokenId, - apiGetAccessTokenPwd); + apiGatewaySetting.getLimoAccessAccount(), + apiGatewaySetting.getLimoAccessPassword()); Map formData = new HashMap<>(); formData.put("action", API_GET_ACCESS_TOKEN); formData.put("itemObject", itemObject); HttpResponseResult result = this.invokeESBFormUrlEncodedAPI( - ESB_LIMO_SERVER, - API_MAIN_HANDLER, + apiGatewaySetting.getLimoProxyUrl(), + apiGatewaySetting.getLimoMainHandleUrl(), formData, "POST", null); @@ -538,10 +522,12 @@ public class MGMLimoApiService { if (Strings.CI.equals("200", status)) { token = resultObj.getString("token"); - ESB_LIMO_ACCESS_TOKEN = token; + LIMO_ACCESS_TOKEN = token; + LIMO_EXPIRED_TIME = LocalDateTime.now().plusSeconds(apiGatewaySetting.getLimoTokenExpirySecond()); } else { token = null; - ESB_LIMO_ACCESS_TOKEN = null; + LIMO_ACCESS_TOKEN = null; + LIMO_EXPIRED_TIME = LocalDateTime.now(); } } } @@ -557,8 +543,12 @@ public class MGMLimoApiService { Map requestHeader) throws Exception { HttpRequestDefine requestDef = new HttpRequestDefine(); - String requestUrl = requestLink; + String requestUrl = serverUrl+requestLink; Map headers = (requestHeader == null ? new HashMap<>() : new HashMap<>(requestHeader)); + //-- set proxy authorization + headers.put("application-id", apiGatewaySetting.getLimoAppId()); + headers.put("Authorization", apiGatewaySetting.getLimoAppSecret()); + //-- String requestBody = buildFormUrlEncodedBody(formData); requestDef.setRequestUrl(requestUrl); @@ -589,9 +579,8 @@ public class MGMLimoApiService { } String responseContent = httpResult.getResponseContent(); - int responseLength = (responseContent == null ? 0 : responseContent.length()); LogUtil.info(MGMLimoApiService.class, "[invokeFormUrlEncodedAPI] Limo action:" + (formData == null ? null : formData.get("action")) - + " Limo ResponseLength:" + responseLength + + " Limo Response:" + responseContent + " Status=" + httpResult.getStatusCode()); httpResult.setRequestContent(requestBody); diff --git a/src/main/java/com/mgm/mcrm/common/utils/EncryptUtil.java b/src/main/java/com/mgm/mcrm/common/utils/EncryptUtil.java index 7480689..c54adfe 100644 --- a/src/main/java/com/mgm/mcrm/common/utils/EncryptUtil.java +++ b/src/main/java/com/mgm/mcrm/common/utils/EncryptUtil.java @@ -3,10 +3,18 @@ package com.mgm.mcrm.common.utils; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; - +import java.security.SecureRandom; +import java.util.Base64; + +import javax.crypto.Cipher; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; public class EncryptUtil { + private static final String AES_TRANSFORMATION = "AES/GCM/NoPadding"; + private static final int GCM_IV_LENGTH = 12; + private static final int GCM_TAG_LENGTH = 16; public static String sha256(String plainString) { String cipherString = null; try { @@ -26,4 +34,43 @@ public class EncryptUtil { return cipherString; } + /** + * 与前端 cfgSettingCrypto.normalizeSecret 对齐:去空白、去 UTF-8 BOM, + * 避免 properties 或编辑器带入不可见字符导致与客户端 SHA-256 密钥不一致。 + */ + public static String normalizeAllowKey(String secret) { + if (secret == null) { + return null; + } + String s = secret.trim(); + if (!s.isEmpty() && s.charAt(0) == '\uFEFF') { + s = s.substring(1); + } + return s; + } + + public static String aesEncryptToBase64(String plainText, String secret) { + if (plainText == null || secret == null || secret.isEmpty()) { + return null; + } + try { + secret = normalizeAllowKey(secret); + if (secret == null || secret.isEmpty()) { + return null; + } + byte[] keyBytes = MessageDigest.getInstance("SHA-256").digest(secret.getBytes(StandardCharsets.UTF_8)); + SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); + byte[] iv = new byte[GCM_IV_LENGTH]; + new SecureRandom().nextBytes(iv); + Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION); + GCMParameterSpec gcmSpec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv); + cipher.init(Cipher.ENCRYPT_MODE, keySpec, gcmSpec); + byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(iv) + ":" + Base64.getEncoder().encodeToString(encrypted); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + } diff --git a/src/main/java/com/mgm/mcrm/common/utils/ExcelUtils.java b/src/main/java/com/mgm/mcrm/common/utils/ExcelUtils.java index 9bf6200..c4bc4ce 100644 --- a/src/main/java/com/mgm/mcrm/common/utils/ExcelUtils.java +++ b/src/main/java/com/mgm/mcrm/common/utils/ExcelUtils.java @@ -773,22 +773,31 @@ public class ExcelUtils { public static void exportToResponse(ExportDefine reportDef, String reportFileName, Map> exportData,Map> groupKeys, HttpServletResponse response, HttpServletRequest request) throws Exception { File file = ExcelUtils.exportEXCEL(reportDef,exportData,groupKeys); + String origin = request.getHeader("Origin"); + String sanitizedOrigin = NetworkUtil.encodePathWithSpring(origin); response.reset(); - String basePath = SysCommon.runtime_path + "\\"+SysCommon.runtimeName+"\\tmp\\"; - String filePath = file.getCanonicalPath(); - basePath = basePath.replace("/", "\\"); - filePath = filePath.replace("/", "\\"); - if (!filePath.startsWith(basePath)) { - throw new SecurityException("Invalid file path"); - } - response.setHeader("Access-Control-Allow-Headers", "Content-Type"); - response.setHeader("Access-Control-Expose-Headers", "Location"); - response.setHeader("Access-Control-Expose-Headers", "content-disposition"); - response.setContentType("application/vnd.ms-excel; charset=utf-8"); - response.setHeader("content-disposition", - "attachment;filename=" + (StringUtils.isNotBlank(reportFileName)?reportFileName:reportDef.getExportFileName())); - response.getOutputStream().write(FileUtils.readFileToByteArray(file)); - ExcelUtils.clearTempFile(file.getName()); + if(SysCommon.isAllowOriginSource(sanitizedOrigin)) { + String basePath = SysCommon.runtime_path + "\\"+SysCommon.runtimeName+"\\tmp\\"; + String filePath = file.getCanonicalPath(); + basePath = basePath.replace("/", "\\"); + filePath = filePath.replace("/", "\\"); + if (!filePath.startsWith(basePath)) { + throw new SecurityException("Invalid file path"); + } + response.setHeader("Access-Control-Allow-Origin",sanitizedOrigin); + response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD"); + response.setHeader("Access-Control-Allow-Headers", "Content-Type"); + response.setHeader("Access-Control-Expose-Headers", "Location"); + response.setHeader("Access-Control-Expose-Headers", "content-disposition"); + response.setContentType("application/vnd.ms-excel; charset=utf-8"); + response.setHeader("content-disposition", + "attachment;filename=" + (StringUtils.isNotBlank(reportFileName)?reportFileName:reportDef.getExportFileName())); + response.getOutputStream().write(FileUtils.readFileToByteArray(file)); + ExcelUtils.clearTempFile(file.getName()); + }else { + LogUtil.warn(ExcelUtils.class, "Origin of ["+sanitizedOrigin+"] is not allowed"); + errorResponse("Download is not allowed from this site",request,response); + } } public static void errorResponse(String errorMessage,HttpServletRequest request, HttpServletResponse response) throws Exception { //response.setContentType("text/html"); diff --git a/src/main/java/com/mgm/mcrm/common/utils/HttpUtil.java b/src/main/java/com/mgm/mcrm/common/utils/HttpUtil.java index b67a8a8..11802e4 100644 --- a/src/main/java/com/mgm/mcrm/common/utils/HttpUtil.java +++ b/src/main/java/com/mgm/mcrm/common/utils/HttpUtil.java @@ -38,7 +38,9 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.io.BufferedReader; import java.io.IOException; +import java.net.URI; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.List; @@ -51,14 +53,14 @@ public class HttpUtil { HttpResponseResult responseResult = new HttpResponseResult(); CloseableHttpClient httpclient=null; CloseableHttpResponse response = null; - String requestUrl =request.getRequestUrl(); - if(!NetworkUtil.isValidURL(requestUrl)){ - throw new SecurityException("Invalid URL of ["+requestUrl+"]"); + String orgRequestUrl =request.getRequestUrl(); + if(!NetworkUtil.isValidURL(orgRequestUrl)){ + throw new SecurityException("Invalid URL of ["+orgRequestUrl+"]"); } - if (!SysCommon.isAllowApiServer(requestUrl)) { + if (!SysCommon.isAllowApiServer(orgRequestUrl)) { throw new SecurityException("URL not allowed"); } - requestUrl = NetworkUtil.encodePathWithSpring(requestUrl); + String requestUrl = NetworkUtil.validateAndSanitizeUrl(orgRequestUrl); try { //-- RequestConfig.Builder configBuilder = RequestConfig.custom(); @@ -98,18 +100,19 @@ public class HttpUtil { HttpClientContext context = HttpClientContext.create(); //-- if(Strings.CI.equals("POST", request.getRequestMethod())) { - HttpPost httppost = new HttpPost(requestUrl); + URI url = new URI(requestUrl); + HttpPost httppost = new HttpPost(url); httppost.setConfig(requestConfig); - if(request.isUpload()!=true) { + if(!request.isUpload()) { if(request.getHeaders()!=null) { Map headermap = request.getHeaders(); for (Map.Entry entry : headermap.entrySet()) { if(!Strings.CI.equals("Content-Length",entry.getKey())){ - httppost.addHeader(entry.getKey(), NetworkUtil.sanitizeHeaderValue(entry.getValue())); + httppost.addHeader(entry.getKey(), NetworkUtil.sanitizeCRLF(entry.getValue())); } } } - httppost.setEntity(new StringEntity(request.getEntityContent(), Charset.forName("UTF-8"))); + httppost.setEntity(new StringEntity(request.getEntityContent(), StandardCharsets.UTF_8)); responseResult.setRequestContent(request.getEntityContent()); response = httpclient.execute(httppost,context); }else { @@ -128,7 +131,7 @@ public class HttpUtil { Map headermap = request.getHeaders(); for (Map.Entry entry : headermap.entrySet()) { if(!Strings.CI.equals("Content-Length",entry.getKey())){ - httpput.addHeader(entry.getKey(), NetworkUtil.sanitizeHeaderValue(entry.getValue())); + httpput.addHeader(entry.getKey(), NetworkUtil.sanitizeCRLF(entry.getValue())); } } } @@ -147,7 +150,7 @@ public class HttpUtil { Map headermap = request.getHeaders(); for (Map.Entry entry : headermap.entrySet()) { if(!Strings.CI.equals("Content-Length",entry.getKey())){ - httpget.addHeader(entry.getKey(), NetworkUtil.sanitizeHeaderValue(entry.getValue())); + httpget.addHeader(entry.getKey(), NetworkUtil.sanitizeCRLF(entry.getValue())); } } } diff --git a/src/main/java/com/mgm/mcrm/common/utils/NetworkUtil.java b/src/main/java/com/mgm/mcrm/common/utils/NetworkUtil.java index ec533f1..c434e61 100644 --- a/src/main/java/com/mgm/mcrm/common/utils/NetworkUtil.java +++ b/src/main/java/com/mgm/mcrm/common/utils/NetworkUtil.java @@ -6,15 +6,10 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; import org.springframework.web.util.UriUtils; -import java.net.InetAddress; -import java.net.NetworkInterface; -import java.net.SocketException; -import java.net.URI; +import java.lang.management.ManagementFactory; +import java.net.*; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.List; +import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -27,7 +22,19 @@ public class NetworkUtil { "10.", "192.168.", "172.16.", "172.17.", "172.18.", "172.19.", "172.20.", "172.21.", "172.22.", "172.23.", "172.24.", "172.25.", "172.26.", "172.27.", "172.28.", "172.29.", "172.30.", "172.31." + ); + + private static final Set ALLOWED_PROTOCOLS = new HashSet<>( + Arrays.asList("http", "https") ); + + + // 禁止的端口 + private static final Set BLOCKED_PORTS = new HashSet<>( + Arrays.asList(22, 23, 25, 53, 110, 143, 3306, 5432, 6379, 27017) + ); + + public static void setInternalIpPattern(String patternConfig) { interalIpPatterns.clear(); @@ -167,13 +174,13 @@ public class NetworkUtil { Enumeration address = ni.getInetAddresses(); while (address.hasMoreElements()) { ip = address.nextElement(); - if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 外网IP + if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && !ip.getHostAddress().contains(":")) {// 外网IP netip = ip.getHostAddress(); ips.add(netip); finded = true; break; } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() - && ip.getHostAddress().indexOf(":") == -1) {// 内网IP + && !ip.getHostAddress().contains(":")) {// 内网IP localip = ip.getHostAddress(); ips.add(localip); } @@ -188,7 +195,7 @@ public class NetworkUtil { * @return the real ip */ public static String getRealIp() { - return getRealIps().iterator().next(); + return Objects.requireNonNull(getRealIps()).getFirst(); } /** @@ -202,22 +209,22 @@ public class NetworkUtil { // 外网IP String netip = null; try { - Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces(); + Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces(); InetAddress ip = null; // 是否找到外网IP boolean finded = false; while (netInterfaces.hasMoreElements() && !finded) { NetworkInterface ni = (NetworkInterface) netInterfaces.nextElement(); - Enumeration address = ni.getInetAddresses(); + Enumeration address = ni.getInetAddresses(); while (address.hasMoreElements()) { ip = (InetAddress) address.nextElement(); - if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) { + if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && !ip.getHostAddress().contains(":")) { // 外网IP netip = ip.getHostAddress(); finded = true; break; } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() - && ip.getHostAddress().indexOf(":") == -1) { + && !ip.getHostAddress().contains(":")) { // 内网IP localip = ip.getHostAddress(); } @@ -227,7 +234,7 @@ public class NetworkUtil { e.printStackTrace(); } - if (netip != null && !"".equals(netip)) { + if (netip != null && !netip.isEmpty()) { return netip; } else { return localip; @@ -350,10 +357,7 @@ public class NetworkUtil { if(!"https".equals(scheme) && !"http".equals(scheme)) { return false; } - if(host.matches("\\d+\\.\\d+\\.\\d+\\.\\d+")) { - return false; - } - return true; + return !host.matches("\\d+\\.\\d+\\.\\d+\\.\\d+"); } catch (Exception e) { return false; } @@ -378,4 +382,116 @@ public class NetworkUtil { value= value.replace("\r", "").replace("\n", ""); return UriUtils.encodePath(value, StandardCharsets.UTF_8); } + public static String sanitizeCRLF(String input){ + if(input==null) + return null; + return input.replaceAll("[\r\n]+", ""); + } + + public static String validateAndSanitizeUrl(String userInputUrl) throws SecurityException { + if (userInputUrl == null || userInputUrl.trim().isEmpty()) { + throw new SecurityException("URL should not be blank"); + } + + try { + // 1. 解析URL + URL url = new URI(userInputUrl).toURL(); + + // 2. 验证协议 + validateProtocol(url.getProtocol()); + + // 3. 验证主机名/IP + String host = url.getHost(); + validateHost(host); + + // 4. 验证端口 + int port = url.getPort(); + if (port == -1) { + port = url.getDefaultPort(); + } + validatePort(port); + + // 5. 验证路径(防止路径遍历) + validatePath(url.getPath()); + + // 6. 返回净化后的URL(标准化) + return sanitizeUrl(url); + + } catch (Exception e) { + throw new SecurityException("Invalid URL: " + e.getMessage(), e); + } + } + + private static void validateProtocol(String protocol) throws SecurityException { + if (!ALLOWED_PROTOCOLS.contains(protocol.toLowerCase())) { + throw new SecurityException("No support protocol: " + protocol + ", only for HTTP/HTTPS"); + } + } + + private static void validateHost(String host) throws SecurityException { + if (host == null || host.isEmpty()) { + throw new SecurityException("Host name can not be empty"); + } + + // 解析IP并验证 + try { + InetAddress address = InetAddress.getByName(host); + String ip = address.getHostAddress(); + + // 检查是否为回环地址(Debug 模式下允许,便于本地联调) + if (address.isLoopbackAddress() && !isDebugMode()) { + throw new SecurityException("Forbit loopback address"); + } + + } catch (UnknownHostException e) { + throw new SecurityException("Invalid host: " + host); + } + } + + private static boolean isDebugMode() { + if (SysCommon.appDebugLog) { + return true; + } + return ManagementFactory.getRuntimeMXBean().getInputArguments().stream() + .anyMatch(arg -> arg.contains("-agentlib:jdwp") || arg.contains("-Xrunjdwp")); + } + + private static void validatePort(int port) throws SecurityException { + // 检查是否为禁止端口 + if (BLOCKED_PORTS.contains(port)) { + throw new SecurityException("Invalid Port: " + port); + } + } + + private static void validatePath(String path) throws SecurityException { + if (path == null) return; + + // 防止路径遍历攻击 + if (path.contains("..")) { + throw new SecurityException("Invalid path contains '..'"); + } + if (path.toLowerCase().contains("://")) { + throw new SecurityException("Invalid path prefix"); + } + } + + private static String sanitizeUrl(URL url) { + StringBuilder sb = new StringBuilder(); + sb.append(url.getProtocol()).append("://"); + sb.append(url.getHost()); + + int port = url.getPort(); + if (port != -1 && port != url.getDefaultPort()) { + sb.append(":").append(port); + } + + sb.append(url.getPath()); + + String query = url.getQuery(); + if (query != null && !query.isEmpty()) { + sb.append("?").append(query); + } + return sb.toString(); + } + } diff --git a/src/main/java/com/mgm/mcrm/common/utils/SOAPUtil.java b/src/main/java/com/mgm/mcrm/common/utils/SOAPUtil.java index e5fc23a..2d8c4a1 100644 --- a/src/main/java/com/mgm/mcrm/common/utils/SOAPUtil.java +++ b/src/main/java/com/mgm/mcrm/common/utils/SOAPUtil.java @@ -1,12 +1,14 @@ package com.mgm.mcrm.common.utils; import com.mgm.mcrm.common.constants.SysCommon; +import com.mgm.mcrm.common.dtos.config.ApiGatewaySetting; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.Marshaller; import jakarta.xml.bind.Unmarshaller; import jakarta.xml.soap.*; import org.apache.commons.lang3.StringUtils; +import org.apache.hc.core5.http.message.BasicHeader; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.NodeList; @@ -15,6 +17,7 @@ import javax.xml.namespace.QName; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; +import java.security.KeyStore; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Iterator; @@ -37,7 +40,7 @@ public class SOAPUtil { * 参考---》http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?op=getWeatherbyCityName */ - public static String getWebServiceSOAP(String url,String action,StringBuffer sendSoapString){ + public static String getWebServiceSOAP(String url, String action, StringBuffer sendSoapString, ApiGatewaySetting apiGatewaySetting){ String soap = sendSoapString.toString(); if (soap == null) { return null; @@ -58,8 +61,12 @@ public class SOAPUtil { conn.setRequestProperty("Content-Length", Integer.toString(soap.length())); conn.setRequestProperty("Content-Type", "text/xml; charset=GBK"); - conn.setConnectTimeout(SysCommon.apiConfig.getTimeout_OWS()); - conn.setReadTimeout(SysCommon.apiConfig.getTimeout_OWS()); + //-- set proxy auth code + conn.setRequestProperty("application-id",apiGatewaySetting.getOperaAppId()); + conn.setRequestProperty("Authorization",apiGatewaySetting.getOperaAppSecret()); + //-- + conn.setConnectTimeout(apiGatewaySetting.getRequestTimeout()); + conn.setReadTimeout(apiGatewaySetting.getRequestTimeout()); // 调用的接口方法是 conn.setRequestProperty("SOAPAction",action); OutputStream os = conn.getOutputStream(); @@ -88,7 +95,7 @@ public class SOAPUtil { return null; } - public static String getWebServiceSOAPUTF(String url,String action,StringBuffer sendSoapString){ + public static String getWebServiceSOAPUTF(String url,String action,StringBuffer sendSoapString, ApiGatewaySetting apiGatewaySetting){ String soap = sendSoapString.toString(); if (soap == null) { return null; @@ -109,6 +116,10 @@ public class SOAPUtil { conn.setRequestProperty("Content-Length", Integer.toString(soap.length())); conn.setRequestProperty("Content-Type", "text/xml; charset=utf-8"); + //-- set proxy auth code + conn.setRequestProperty("application-id",apiGatewaySetting.getOperaAppId()); + conn.setRequestProperty("Authorization",apiGatewaySetting.getOperaAppSecret()); + //-- // 调用的接口方法是 conn.setRequestProperty("SOAPAction",action); OutputStream os = conn.getOutputStream(); @@ -153,33 +164,40 @@ public class SOAPUtil { } private static final TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() { + private X509TrustManager defaultTrustManager=null; + { + try { + // 获取 JVM 默认的 TrustManager 作为核心校验逻辑 + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init((KeyStore) null); // 使用系统的默认信任库 + for (TrustManager tm : tmf.getTrustManagers()) { + if (tm instanceof X509TrustManager) { + defaultTrustManager = (X509TrustManager) tm; + } + } + }catch(Exception err){ + LogUtil.error(SOAPUtil.class,"Default X509TrustManager not found["+err.getMessage()+"]"); + } + } @Override public X509Certificate[] getAcceptedIssuers() { - return new java.security.cert.X509Certificate[] {}; + return defaultTrustManager.getAcceptedIssuers(); } @Override - public void checkServerTrusted(X509Certificate[] chain, String arg1) throws CertificateException { - if (chain == null || chain.length == 0) { - throw new IllegalArgumentException("No server certificates provided"); - } - for (X509Certificate cert : chain) { - cert.checkValidity(); - } + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + defaultTrustManager.checkServerTrusted(chain,authType); } @Override - public void checkClientTrusted(X509Certificate[] chain, String arg1) throws CertificateException { - if (chain == null || chain.length == 0) { - throw new CertificateException("No client certificates provided"); - } - for (X509Certificate cert : chain) { - cert.checkValidity(); // 检查证书是否在有效期内 - } + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + defaultTrustManager.checkClientTrusted(chain,authType); } }}; + + private static final HostnameVerifier checkSoapServer = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { boolean isServerAllow = SysCommon.isAllowSoapServer(hostname); diff --git a/src/main/java/com/mgm/mcrm/common/utils/SendEmail.java b/src/main/java/com/mgm/mcrm/common/utils/SendEmail.java index 8c20b89..574de03 100644 --- a/src/main/java/com/mgm/mcrm/common/utils/SendEmail.java +++ b/src/main/java/com/mgm/mcrm/common/utils/SendEmail.java @@ -453,7 +453,6 @@ public class SendEmail { userName = !StringUtils.isEmpty(userName)?userName:""; String tUser = userId+" "+userName; String approvalStatus = EumnApprovalStatus.getApprovalStatusByDBValue(book.getApproveStatus()); - content2 += ""+ "" @@ -562,6 +561,171 @@ public class SendEmail { return emailObj; } + + public static Email sendCreateR2Y2NewEmail(Bookings book,BookingsView bookView,Email emailObj,String userId,String userName,boolean isOverBook,boolean isConnectRoom,String emailTo) { + String content1 = emailNewApprovalTemp; + try { + JavaMailSenderImpl sender = new JavaMailSenderImpl(); + if(StringUtils.isEmpty(mail_smtp_host)) { + mail_smtp_host = SysCommon.apiConfig.getHost_SMTP(); + } + mail_port = Integer.valueOf(SysCommon.apiConfig.getPort_SMTP()); + if(StringUtils.isEmpty(mail_sender)) { + mail_sender = SysCommon.apiConfig.getUsername_SMTP(); + } + if(StringUtils.isEmpty(mail_sender_password)) { + mail_sender_password = SysCommon.apiConfig.getPassword_SMTP(); + } + if(!StringUtils.isEmpty(emailTo)) { + mail_to = emailTo; + }else { + mail_to = SysCommon.apiConfig.getSend_to_SMTP(); + } + sender.setHost(mail_smtp_host); + sender.setPort(mail_port); // 默认就是25 + sender.setUsername(mail_sender); + sender.setPassword(mail_sender_password);// + sender.setDefaultEncoding("UTF-8"); + + // 配置文件对象 + Properties props = new Properties(); + props.put("mail.smtp.auth", "false"); // 是否进行验证 + Session session = Session.getInstance(props); + sender.setSession(session); // 为发送器指定会话 + + MimeMessage mail = sender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(mail, true); + + String mainTitle; + if(isConnectRoom) { + mainTitle = "[R2/Y2]"; + }else if(isOverBook) { + mainTitle = "[Overbook]"; + }else { + mainTitle = "[Urgent]"; + } + helper.setSubject(mainTitle+"Booking Creation from Mobile CRM APP"); // 标题 + helper.setFrom(mail_sender); // 来自 + + SimpleDateFormat sf = new SimpleDateFormat("dd-MMM-yyyy HH:mm"); + SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy"); + String RequestDate = sf.format(new Date()); + + String content2 = ""; + String tTime = RequestDate; + userId = !StringUtils.isEmpty(userId)?userId:""; + userName = !StringUtils.isEmpty(userName)?userName:""; + String tUser = userId+" "+userName; + String approvalStatus = EumnApprovalStatus.getApprovalStatusByDBValue(book.getApproveStatus()); + + + content2 += ""+ + "" + +approvalStatus+ + " "+ + "" + +bookView.getPlayerId()+ + " " + + "" + +bookView.getMemberLastName()+ + " " + + "" + +(bookView.getMemberTier()!=null?bookView.getMemberTier():"")+ + " " + + "" + +(bookView.getPropertyName()!=null?bookView.getPropertyName():"")+ + " " + + "" + +(bookView.getRoom()!=null?bookView.getRoom():"")+ + " "+ + "" + +(bookView.getSharerName()!=null?bookView.getSharerName():"")+ + " " + + "" + +"New"+ + " "+ + "" + +format.format(bookView.getCheckinDate())+ + " " + + "" + +format.format(bookView.getCheckoutDate())+ + " " + + "" + +(bookView.getBookerTeam()!=null?bookView.getBookerTeam():"")+ + " " + + "" + +bookView.getCreatedBy()+ + " " + + "" + +bookView.getCreatedByName()+ + " " + + "" + +(bookView.getApprovalRemark()!=null?bookView.getApprovalRemark():"")+ + " " + + "" + +(bookView.getRemark()!=null?bookView.getRemark():"")+ + " " + + " "; + + String mailContent = isConnectRoom?"Please create TT/YY booking for below approved booking

":""; + content1 = content1.replace("{{tbody}}", content2); + content1 = content1.replace("{{ttime}}", tTime); + content1 = content1.replace("{{tuser}}", tUser); + content1 = content1.replace("{{content}}", mailContent); + helper.setText(content1,true); // 邮件内容,第二个参数指定发送的是HTML格式 + JSONObject emailJson = new JSONObject(); + emailJson.put("approvalStatus", approvalStatus); + emailJson.put("memberId", bookView.getPlayerId()); + emailJson.put("memberName", bookView.getMemberLastName()); + emailJson.put("tier", bookView.getMemberTier()); + emailJson.put("property", bookView.getPropertyName()); + emailJson.put("roomType", bookView.getRoom()); + emailJson.put("type", "New"); + emailJson.put("checkin", format.format(bookView.getCheckinDate())); + emailJson.put("checkout", format.format(bookView.getCheckoutDate())); + emailJson.put("bookerTeam", bookView.getBookerTeam()); + emailJson.put("bookerId", bookView.getCreatedBy()); + emailJson.put("bookerName", bookView.getCreatedByName()); + emailJson.put("approvalRemark", bookView.getApprovalRemark()); + emailJson.put("remark", bookView.getRemark()); + + emailObj.setSendObject(emailJson.toJSONString()); + emailObj.setContent(content1); + emailObj.setSendTo(mail_to); + String[] mailToArr = StringUtils.split(mail_to,";"); + if(mailToArr.length>1) { + for (String str : mailToArr) { + helper.setTo(str); + try { + sender.send(mail); // 发送 + emailObj.setStatus(1); + } catch (Exception e) { + emailObj.setMessage("Send Email to "+str+" Failed:"+e.getMessage()); + emailObj.setStatus(0); + } + + } + }else { + helper.setTo(mail_to); + try { + sender.send(mail); // 发送 + emailObj.setStatus(1); + LogUtil.info(SendEmail.class, "---mgmEmail.sendApprovalNewEmail--OK!"); + } catch (Exception e) { + emailObj.setMessage("Send Email to "+mail_to+" Failed:"+e.getMessage()); + emailObj.setStatus(0); + LogUtil.error(SendEmail.class, "---mgmEmail.sendApprovalNewEmail--Failed!"+e.getMessage()); + } + } + + } catch (Exception e) { + LogUtil.error(SendEmail.class,"---mgmEmail.sendApprovalNewEmail--Exception-->>"+e.getMessage()); + emailObj.setMessage(e.getMessage()); + emailObj.setStatus(0); + } + + return emailObj; + } public static Email sendApprovalExtendEmail(Bookings book,BookingsView originBook,Email emailObj,String userId,String userName,BookingsView viewBook,boolean isOverBook,String emailTo) { String content1 = emailApprovalTemp; @@ -590,7 +754,7 @@ public class SendEmail { // 配置文件对象 Properties props = new Properties(); - props.put("mail.smtp.auth", "false"); // 是否进行验证 + props.put("mail.smtp.auth", "true"); // 是否进行验证 Session session = Session.getInstance(props); sender.setSession(session); // 为发送器指定会话 @@ -623,7 +787,7 @@ public class SendEmail { changeCheckInDate = viewBook.getCheckinDate()!=null?format.format(viewBook.getCheckinDate()):""; } - + content2 += ""+ "" +approvalStatus+ @@ -928,7 +1092,7 @@ public class SendEmail { MimeMessageHelper helper = new MimeMessageHelper(mail, true); String subject = emailTemplate.getSubject(); - subject = StringUtils.replace(subject, "[TODAY]",DateTimeUtil.getCurrentDate("dd MMM")); + subject = Strings.CI.replace(subject, "[TODAY]", DateTimeUtil.getCurrentDate("dd MMM")); helper.setSubject(subject); // 标题 helper.setFrom(mail_sender); // 来自 diff --git a/src/main/java/com/mgm/mcrm/limo/controller/LimoController.java b/src/main/java/com/mgm/mcrm/limo/controller/LimoController.java index b1a56b3..46147ae 100644 --- a/src/main/java/com/mgm/mcrm/limo/controller/LimoController.java +++ b/src/main/java/com/mgm/mcrm/limo/controller/LimoController.java @@ -44,21 +44,19 @@ public class LimoController { private MgmUserService userService; @RequestMapping(value = "/search_limo", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) - public List limoRecordSearch(HttpServletRequest request, @RequestBody Map params) throws Exception { - HashMap member = (HashMap)params.get("member"); + public List limoRecordSearch(HttpServletRequest request, @RequestBody Map params) throws Exception { + HashMap member = (HashMap) params.get("member"); String playerId = String.valueOf(member.get("playerId")); - List response = new ArrayList<>(); - boolean havePermission = sysGroupPermissionService.checkAppPermission(request,"10001_08"); + boolean havePermission = sysGroupPermissionService.checkAppPermission(request, "10001_08"); + List bookingsByPlayerId = null; if (havePermission) { - if(!SysCommon.mgmApiMockup) + if (!SysCommon.mgmApiMockup) limoService.getTodayBooking(playerId); - response = limoService.getBookingsByPlayerId(playerId, request); + bookingsByPlayerId = limoService.getBookingsByPlayerId(playerId, request); } - - - return response; + return bookingsByPlayerId; } @GetMapping("/getTripPriceList") diff --git a/src/main/java/com/mgm/mcrm/limo/services/LimoService.java b/src/main/java/com/mgm/mcrm/limo/services/LimoService.java index 3b85d22..be0cbb4 100644 --- a/src/main/java/com/mgm/mcrm/limo/services/LimoService.java +++ b/src/main/java/com/mgm/mcrm/limo/services/LimoService.java @@ -4,14 +4,12 @@ import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.parser.Feature; import com.auth0.jwt.JWT; -import com.mgm.mcrm.appsetting.services.SettingService; import com.mgm.mcrm.common.constants.SysCommon; import com.mgm.mcrm.common.dtos.BaseApiResponse; import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; -import com.mgm.mcrm.common.dtos.mgm_api.PlayerProfileApiResult; import com.mgm.mcrm.common.dtos.mgm_api.PlayerProfileEntry; import com.mgm.mcrm.common.dtos.mgm_api.UserInfoApiRecord; -import com.mgm.mcrm.common.services.MGMApiService; +import com.mgm.mcrm.common.services.MGMEsbApiService; import com.mgm.mcrm.common.utils.LogUtil; import com.mgm.mcrm.limo.dtos.request.GetLimoFlightDetailsRequest; import com.mgm.mcrm.limo.dtos.request.GetLimoTotalAmountRequest; @@ -20,13 +18,12 @@ import com.mgm.mcrm.limo.dtos.request.InternalUpdateLimoBookingRequest; import com.mgm.mcrm.limo.dtos.request.RequestPlayerEsbBookerRequest; import com.mgm.mcrm.limo.dtos.request.*; import com.mgm.mcrm.limo.dtos.response.*; -import com.mgm.mcrm.common.services.MGMEsbApiService; +import com.mgm.mcrm.common.services.MGMEsbLimoApiService; import com.mgm.mcrm.common.services.MGMLimoApiService; import com.mgm.mcrm.player.services.PlayerService; import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; -import org.apache.poi.util.StringUtil; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.orm.ObjectOptimisticLockingFailureException; @@ -52,8 +49,6 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.*; import java.util.function.Function; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; @Slf4j @@ -70,13 +65,11 @@ public class LimoService { @Resource private LimoBookingDraftRepository limoBookingDraftRepository; @Resource - private MGMEsbApiService mgmEsbApiService; + private MGMEsbLimoApiService mgmEsbLimoApiService; @Resource private MGMLimoApiService mgmLimoApiService; @Resource - private MGMApiService mgmApiService; - @Resource - private SettingService settingService; + private MGMEsbApiService mgmEsbApiService; @Resource private PlayerService playerService; @Value("${limo.booking-draft.expiry-seconds:120}") @@ -102,7 +95,7 @@ public class LimoService { String token = requestHeader.getHeader("Authorization"); String userId = JWT.decode(token).getClaim("login_id").asString(); String userName = userInfo.getUserName(); - Integer autoConfirm = 0; + int autoConfirm = 0; StringBuilder missing = new StringBuilder(); if (request.getPlayerId() == null) { @@ -140,7 +133,7 @@ public class LimoService { request.getPickUpPointId(), request.getDropOffPointId(), request.getExtraOffPointId(), - parseIntegerOrDefault(request.getHour(), 0), + parseIntegerOrDefault(request.getHour()), request.getTripType(), request.getServiceType(), request.getIsHourlyRate(), @@ -241,7 +234,7 @@ public class LimoService { entity.setCreatedAt(nowTs); entity.setUpdatedAt(nowTs); - HttpResponseResult httpResponseResult = mgmEsbApiService.invokeSimpleLimoBookingQualificationStatus( + HttpResponseResult httpResponseResult = mgmEsbLimoApiService.invokeSimpleLimoBookingQualificationStatus( request.getPlayerId(), request.getBookingType(), formatEsbBookingDate(request.getPickUpDatetime())); @@ -459,7 +452,7 @@ public class LimoService { request.getPickupPointId(), request.getDropOffPointIds(), request.getExtraDropOffPointId(), - parseIntegerOrDefault(String.valueOf(request.getHour()), 0), + parseIntegerOrDefault(String.valueOf(request.getHour())), request.getTripType(), request.getServiceType(), request.getIsHourlyRate(), @@ -749,7 +742,7 @@ public class LimoService { String greetTypeIds = defaultString(returnLimoBooking.getGreetType(), "").trim(); String greetTypeNames = resolveGreetTypeNames(greetTypeIds, greetTypeNameMap); entity.setGreetTypeId(greetTypeIds); - if (StringUtils.hasText(greetTypeNames) || entity == null) { + if (StringUtils.hasText(greetTypeNames)) { entity.setGreetType(greetTypeNames); } @@ -805,7 +798,7 @@ public class LimoService { //通过ESB接口获取到Limo Booker,即为前端Approver中可选信息 public BaseApiResponse> getPlayerEsbBooker(RequestPlayerEsbBookerRequest request) throws Exception { - HttpResponseResult httpResponseResult = mgmEsbApiService.invokeRequestPlayerLimoBooker(request); + HttpResponseResult httpResponseResult = mgmEsbLimoApiService.invokeRequestPlayerLimoBooker(request); if (httpResponseResult.getStatusCode() != 200) { return BaseApiResponse.internalServerError( "ESB HTTP error: " + httpResponseResult.getStatusCode()); @@ -837,7 +830,7 @@ public class LimoService { //通过ESB接口 获取到当前Booking是否够分,不够分需要填入Overrider public GetLimoBookingQualificationResponse checkBookingQualification(GetLimoBookingQualificationRequest request) throws Exception { - HttpResponseResult httpResponseResult = mgmEsbApiService.invokeSimpleLimoBookingQualificationStatus( + HttpResponseResult httpResponseResult = mgmEsbLimoApiService.invokeSimpleLimoBookingQualificationStatus( request.getPlayerId(), request.getBookingType(), request.getBookingDate()); if (httpResponseResult.getStatusCode() != 200) { @@ -856,7 +849,7 @@ public class LimoService { //通过ESB接口 获取到Overriver,当Booking不够分的时候填入Overrider public GetOverriderInfoResponse getLimoOverriderData(GetLimoBookingGetOverriderRequest request) throws Exception { - HttpResponseResult httpResponseResult = mgmEsbApiService.invokeLimoOverrider(request.getOverriderInfo()); + HttpResponseResult httpResponseResult = mgmEsbLimoApiService.invokeLimoOverrider(request.getOverriderInfo()); if (httpResponseResult.getStatusCode() != 200) { throw new IllegalStateException( @@ -875,7 +868,7 @@ public class LimoService { //通过ESB接口 获取到LimoEmployeeInfo public GetLimoEmployeeInfoResponse getLimoEmployeeData(GetLimoEmployeeInfoRequest request) throws Exception { - HttpResponseResult httpResponseResult = mgmEsbApiService.invokeRequestEmployeeInfo(request.getEmployeeId()); + HttpResponseResult httpResponseResult = mgmEsbLimoApiService.invokeRequestEmployeeInfo(request.getEmployeeId()); if (httpResponseResult.getStatusCode() != 200) { throw new IllegalStateException( @@ -894,7 +887,7 @@ public class LimoService { LimoBookings target = null; List matchBookings = limoBookingsRepository.findByLimoBookingId(limoBookingId); if(matchBookings!=null && !matchBookings.isEmpty()){ - target = matchBookings.get(0); + target = matchBookings.getFirst(); for(int i=1;i item.getId() != null && item.getId().longValue() == tripPriceId.longValue()) + .filter(item -> item.getId() != null && item.getId() == tripPriceId.longValue()) .map(LimoTripPriceItem::getHourlyRate) .filter(Objects::nonNull) .findFirst() @@ -2050,14 +2043,14 @@ public class LimoService { return value == null ? "" : value.replace("_", "").replace(" ", "").trim().toUpperCase(Locale.ROOT); } - private static Integer parseIntegerOrDefault(String value, Integer defaultValue) { + private static Integer parseIntegerOrDefault(String value) { if (!StringUtils.hasText(value)) { - return defaultValue; + return 0; } try { return Integer.parseInt(value.trim()); } catch (NumberFormatException e) { - return defaultValue; + return 0; } } @@ -2264,7 +2257,7 @@ public class LimoService { } String value = confirmationNo.trim(); - int suffixIndex = org.apache.commons.lang3.StringUtils.indexOfIgnoreCase(value, "-S"); + int suffixIndex = org.apache.commons.lang3.Strings.CS.indexOf(value, "-S"); String bookingPart = suffixIndex >= 0 ? value.substring(0, suffixIndex) : value; int firstDigitIndex = 0; diff --git a/src/main/java/com/mgm/mcrm/logging/controllers/SysLogAppController.java b/src/main/java/com/mgm/mcrm/logging/controllers/SysLogAppController.java index eadd001..f82edf4 100644 --- a/src/main/java/com/mgm/mcrm/logging/controllers/SysLogAppController.java +++ b/src/main/java/com/mgm/mcrm/logging/controllers/SysLogAppController.java @@ -12,6 +12,7 @@ import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import org.springframework.web.util.HtmlUtils; @@ -20,14 +21,13 @@ import java.util.Date; @RestController -@CrossOrigin(origins = "*") @RequestMapping("/api") public class SysLogAppController { @Resource private SysLogAppService logService; - @RequestMapping(value = "/system/write_log", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/system/write_log", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> writeAppLog(HttpServletRequest request, @RequestBody(required=true) AppLogDto appLogDto) { // The JWT filter authenticates this route before the controller runs. LoginUserInfo user = JwtTokenUtil.parseUserFromRequest(request); @@ -37,7 +37,7 @@ public class SysLogAppController { return writeLog(request, appLogDto, limit(user.getLoginId(), 64)); } - @PostMapping(value = "/system/write_public_log", produces = "application/json;charset=UTF-8") + @PostMapping(value = "/system/write_public_log", produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> writePublicAppLog(HttpServletRequest request, @RequestBody(required=true) AppLogDto appLogDto) { return writeLog(request, appLogDto, "ANONYMOUS"); diff --git a/src/main/java/com/mgm/mcrm/logging/controllers/SysLogConfirmController.java b/src/main/java/com/mgm/mcrm/logging/controllers/SysLogConfirmController.java index df6af45..56ac7c6 100644 --- a/src/main/java/com/mgm/mcrm/logging/controllers/SysLogConfirmController.java +++ b/src/main/java/com/mgm/mcrm/logging/controllers/SysLogConfirmController.java @@ -7,19 +7,19 @@ import com.mgm.mcrm.logging.services.SysLogConfirmService; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController -@CrossOrigin(origins = "*") @RequestMapping("/api") public class SysLogConfirmController { @Resource private SysLogConfirmService logService; - @RequestMapping(value = "/app/log_confirm/save_confirm_log", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/log_confirm/save_confirm_log", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getOperaBookingByConfirmationNo(HttpServletRequest request, @RequestBody MobileConfirmLogRequest params) { BaseApiResponse result =logService.saveConfirm(params); diff --git a/src/main/java/com/mgm/mcrm/notification/controllers/EmailTemplateController.java b/src/main/java/com/mgm/mcrm/notification/controllers/EmailTemplateController.java index 8f3fa1f..b7729d8 100644 --- a/src/main/java/com/mgm/mcrm/notification/controllers/EmailTemplateController.java +++ b/src/main/java/com/mgm/mcrm/notification/controllers/EmailTemplateController.java @@ -7,6 +7,7 @@ import com.mgm.mcrm.notification.services.EmailTemplateService; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -14,7 +15,6 @@ import java.util.List; @RestController -@CrossOrigin(origins = "*") @RequestMapping("/api") public class EmailTemplateController { @@ -22,14 +22,14 @@ public class EmailTemplateController { private EmailTemplateService emailTemplateService; - @RequestMapping(value = "/web/email_template/send", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/email_template/send", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> sendEmailTemplates(HttpServletRequest request, @RequestBody CfgEmailTemplate emailTemplate) { //-- BaseApiResponse result = emailTemplateService.sendEmailTemplates(emailTemplate,request); return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/web/email_template/generate", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/email_template/generate", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity>> generateEmailTemplates(HttpServletRequest request, @RequestBody(required=true) GenerateBookingEmailRequest generateRequest) { BaseApiResponse> result = new BaseApiResponse<>(); //-- diff --git a/src/main/java/com/mgm/mcrm/notification/controllers/NotificationController.java b/src/main/java/com/mgm/mcrm/notification/controllers/NotificationController.java index 822ba6d..c8800f0 100644 --- a/src/main/java/com/mgm/mcrm/notification/controllers/NotificationController.java +++ b/src/main/java/com/mgm/mcrm/notification/controllers/NotificationController.java @@ -21,6 +21,7 @@ import jakarta.annotation.Resource; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -48,9 +49,9 @@ public class NotificationController { boolean viewNotification = false; userPermissions = sysGroupPermissionService.getRouterPermissionsByGroupName(groupName); - if(userPermissions.size()>0) { + if(!userPermissions.isEmpty()) { for(SysRouterPermission per:userPermissions) { - if(StringUtils.equals(per.getId(), "10001_06") ) viewNotification = true; + if(Strings.CI.equals(per.getId(), "10001_06") ) viewNotification = true; } } if (viewNotification) { @@ -72,7 +73,7 @@ public class NotificationController { return notificationService.countNotificationByUserId(userId,groupName); } - @PostMapping("/change_status") + @PostMapping(value="/change_status", produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ChangeNotificationStatusResponse changeStatus(@RequestBody ChangeNotificationStatusRequest request) { Long id = request != null ? request.getId() : null; return notificationService.changeStatus(id); @@ -95,12 +96,13 @@ public class NotificationController { @ApiResponse(responseCode = "200", description = "Access Token generated successful"), @ApiResponse(responseCode = "401", description = "Authorization key not math") }) - @PostMapping(value="/send_notification",produces = "application/json;charset=UTF-8") + @PostMapping(value="/send_notification",produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> sendNotification( HttpServletRequest request, @Parameter(description = "Notification request info", required = true) @Valid @RequestBody SendNotificationRequest requestData) { LoginUserInfo requestUser = SysCommon.parseUserFromToken(request); if(requestUser!=null){ + requestData.setRequestorId(requestUser.getLoginId()); BaseApiResponse responseData = notificationService.sendNotificationByRequest(request,requestData); return ResponseEntity.status(HttpStatus.OK).body(responseData); }else{ diff --git a/src/main/java/com/mgm/mcrm/notification/dtos/request/SendNotificationRequest.java b/src/main/java/com/mgm/mcrm/notification/dtos/request/SendNotificationRequest.java index 0236bc6..e8fb144 100644 --- a/src/main/java/com/mgm/mcrm/notification/dtos/request/SendNotificationRequest.java +++ b/src/main/java/com/mgm/mcrm/notification/dtos/request/SendNotificationRequest.java @@ -27,4 +27,6 @@ public class SendNotificationRequest { @Schema(description = "Type of booking ") private String type; + private String requestorId; + } diff --git a/src/main/java/com/mgm/mcrm/notification/services/NotificationService.java b/src/main/java/com/mgm/mcrm/notification/services/NotificationService.java index 0b71aac..630a471 100644 --- a/src/main/java/com/mgm/mcrm/notification/services/NotificationService.java +++ b/src/main/java/com/mgm/mcrm/notification/services/NotificationService.java @@ -1,6 +1,7 @@ package com.mgm.mcrm.notification.services; import com.alibaba.fastjson.JSON; +import com.mgm.mcrm.approval.repositories.ApprovalRoutingDao; import com.mgm.mcrm.authorization.models.SysRouterPermission; import com.mgm.mcrm.authorization.services.SysGroupPermissionService; import com.mgm.mcrm.basesetting.daylight.dtos.response.ProfileDetailResponse; @@ -23,6 +24,7 @@ import jakarta.servlet.http.HttpServletRequest; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.hc.core5.http.Header; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.time.LocalDateTime; @@ -37,6 +39,8 @@ public class NotificationService { private SysGroupPermissionService sysGroupPermissionService; @Resource private HttpService httpService; + @Autowired + private ApprovalRoutingDao approvalRoutingDao; public GetNotificationResponse getNotificationByUserId(String userId){ GetNotificationResponse response = new GetNotificationResponse(); @@ -64,6 +68,10 @@ public class NotificationService { response.setNotificationCount(sysLogNotificationRepository.countByUserIdAndStatus(userId, 0)); count+=sysLogNotificationRepository.countByUserIdAndStatus(userId, 0); } + if (viewApprovalRouting) { + response.setApprovalRoutingCount(approvalRoutingDao.countApprovalList(userId)); + count+=approvalRoutingDao.countApprovalList(userId); + } response.setCount(count); return response; } @@ -103,7 +111,6 @@ public class NotificationService { String url = "/send_notification"; // 获取对应的值 Header[] headers = HttpHeaderBuilder.custom() - .authorization(request.getHeader("Authorization")) .contentType("application/json") .build(); HttpResult responseData = httpService.sslSendNotification(url, headers, requestData); diff --git a/src/main/java/com/mgm/mcrm/player/controllers/PlayerController.java b/src/main/java/com/mgm/mcrm/player/controllers/PlayerController.java index 4c9ded0..e13af11 100644 --- a/src/main/java/com/mgm/mcrm/player/controllers/PlayerController.java +++ b/src/main/java/com/mgm/mcrm/player/controllers/PlayerController.java @@ -19,6 +19,7 @@ import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Strings; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -26,7 +27,6 @@ import java.util.List; import java.util.Map; @RestController -@CrossOrigin(origins = "*") @RequestMapping("/api") public class PlayerController { @@ -36,7 +36,7 @@ public class PlayerController { @Resource private SysSettingService sysSettingService; - @RequestMapping(value = "/app/mgm/player_lookup", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/mgm/player_lookup", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public BaseApiResponse getPlayerProfileByPlayerId(HttpServletRequest request, @RequestBody(required=false) Map params) { String playerId = params.get("playerId"); // String userId = params.get("userId"); @@ -59,7 +59,7 @@ public class PlayerController { return result; } - @RequestMapping(value = "/app/mgm/check_tpd_by_playerId_room_dates", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/mgm/check_tpd_by_playerId_room_dates", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> checkTPDByPlayerIdAndRoomCatAndDates(HttpServletRequest request, @RequestBody(required=true) CheckTPDRequest params) { String playerId = params.getPlayerId(); @@ -95,7 +95,7 @@ public class PlayerController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/app/mgm/check_second_tpd_by_playerId_room_dates", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/mgm/check_second_tpd_by_playerId_room_dates", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> checkSecondRoomTPDByPlayerIdAndRoomCatAndDates(HttpServletRequest request, @RequestBody(required=true) CheckSecondTPDRequest params) { String playerId = params.getPlayerId(); @@ -137,7 +137,7 @@ public class PlayerController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/web/player/lookup", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/player/lookup", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> lookupPlayer(HttpServletRequest request, HttpServletResponse response, @RequestBody WebPlayerLookupRequest params) throws Exception { BaseApiResponse processResult = new BaseApiResponse<>(); @@ -163,7 +163,7 @@ public class PlayerController { return ResponseEntity.status(HttpStatus.OK).body(processResult); } - @RequestMapping(value = "/web/mgm/check_tpd_by_playerId_room_dates", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/mgm/check_tpd_by_playerId_room_dates", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> checkFirstRoomCriteria(HttpServletRequest request, @RequestBody WebCheckPlayerTPDRequest params) { String playerId = params.getPlayerId(); @@ -176,7 +176,7 @@ public class PlayerController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/web/mgm/check_second_tpd_by_playerId_room_dates", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/web/mgm/check_second_tpd_by_playerId_room_dates", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> checkSecondRoomCriteria(HttpServletRequest request, @RequestBody WebCheckPlayerTPDRequest params) { String playerId = params.getPlayerId(); diff --git a/src/main/java/com/mgm/mcrm/player/controllers/TodoController.java b/src/main/java/com/mgm/mcrm/player/controllers/TodoController.java index b6810cc..53eb14f 100644 --- a/src/main/java/com/mgm/mcrm/player/controllers/TodoController.java +++ b/src/main/java/com/mgm/mcrm/player/controllers/TodoController.java @@ -23,6 +23,7 @@ import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Strings; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -31,7 +32,6 @@ import java.util.ArrayList; import java.util.List; @RestController -@CrossOrigin(origins = "*") @RequestMapping("/api") public class TodoController { @@ -47,7 +47,7 @@ public class TodoController { @Resource private SysGroupPermissionService sysGroupPermissionService; - @RequestMapping(value = "/app/todo/get_dashboard_data", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/todo/get_dashboard_data", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getTodoDashboard(HttpServletRequest request, @RequestBody GetDashboardRequest getDashboardRequest) throws Exception { LoginUserInfo loginUser = SysCommon.parseUserFromToken(request); @@ -74,7 +74,7 @@ public class TodoController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/app/todo/get_todolist_details", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/todo/get_todolist_details", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getTodoDetail(HttpServletRequest request, @RequestBody GetTodoDetailRequest getTodoDetailRequest) throws Exception { String playerId = getTodoDetailRequest.getPlayerId(); String todoSegment = getTodoDetailRequest.getTodosegment(); @@ -91,12 +91,13 @@ public class TodoController { }else { result.setSuccess(false); result.setCode(getTodoDetailResponse.getErrorCode()); + result.setMessage(getTodoDetailResponse.getErrorMessage()); } return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/app/todo/get_todolist", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/todo/get_todolist", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> getTodoList(HttpServletRequest request, @RequestBody GetTodoListRequest params) throws Exception { LoginUserInfo loginUser = SysCommon.parseUserFromToken(request); @@ -128,7 +129,7 @@ public class TodoController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/app/todo/change_date", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/todo/change_date", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> changeDate(HttpServletRequest request, @RequestBody TodoChangeDateRequest bookingParams) throws Exception { BaseApiResponse result = new BaseApiResponse<>(); result.setRequestData(bookingParams); @@ -174,7 +175,7 @@ public class TodoController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/app/todo/submit_ETA", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/todo/submit_ETA", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> submitETA(HttpServletRequest request, @RequestBody TodoSubmitETARequest params) throws Exception { LoginUserInfo loginUser = SysCommon.parseUserFromToken(request); @@ -219,7 +220,7 @@ public class TodoController { return ResponseEntity.status(HttpStatus.OK).body(result); } - @RequestMapping(value = "/app/todo/cancel_booking", method = RequestMethod.POST, produces = "application/json;charset=UTF-8") + @RequestMapping(value = "/app/todo/cancel_booking", method = RequestMethod.POST, produces = "application/json;charset=UTF-8",consumes = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> cancelBooking(HttpServletRequest request, @RequestBody TodoCancelBookingRequest cancelRequest) throws Exception { LoginUserInfo loginUser = SysCommon.parseUserFromToken(request); diff --git a/src/main/java/com/mgm/mcrm/player/dtos/request/TodoChangeDateRequest.java b/src/main/java/com/mgm/mcrm/player/dtos/request/TodoChangeDateRequest.java index 2055221..1cdd3cc 100644 --- a/src/main/java/com/mgm/mcrm/player/dtos/request/TodoChangeDateRequest.java +++ b/src/main/java/com/mgm/mcrm/player/dtos/request/TodoChangeDateRequest.java @@ -1,6 +1,7 @@ package com.mgm.mcrm.player.dtos.request; import com.mgm.mcrm.booking.dtos.BookingDateEntry; +import com.mgm.mcrm.player.dtos.UserInfo; import lombok.Data; import java.util.Date; @@ -22,4 +23,5 @@ public class TodoChangeDateRequest { private Long segmentId; private List dateList; private List extendDate; + private UserInfo user; } diff --git a/src/main/java/com/mgm/mcrm/player/services/PlayerService.java b/src/main/java/com/mgm/mcrm/player/services/PlayerService.java index df07e88..402a0ab 100644 --- a/src/main/java/com/mgm/mcrm/player/services/PlayerService.java +++ b/src/main/java/com/mgm/mcrm/player/services/PlayerService.java @@ -10,7 +10,7 @@ import com.mgm.mcrm.common.dtos.BaseApiResponse; import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; import com.mgm.mcrm.common.dtos.mgm_api.PlayerProfileEntry; import com.mgm.mcrm.common.dtos.mgm_api.PlayerTPDApiResult; -import com.mgm.mcrm.common.services.MGMApiService; +import com.mgm.mcrm.common.services.MGMEsbApiService; import com.mgm.mcrm.player.dtos.PlayerTPD; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -26,7 +26,7 @@ import java.util.Random; public class PlayerService { @Resource - private MGMApiService mgmApiService; + private MGMEsbApiService mgmEsbApiService; @Resource private SysSettingService sysSettingService; @@ -39,7 +39,7 @@ public class PlayerService { HttpResponseResult httpResult; try { // Daylight profile 调用不再需要 token,这里直接调用 MGM API - httpResult = mgmApiService.invokeGetPlayerProfile(playerId, userId); + httpResult = mgmEsbApiService.invokeGetPlayerProfile(playerId, userId); if (httpResult.getStatusCode() != 200) { result.setSuccess(false); @@ -102,7 +102,7 @@ public class PlayerService { HttpResponseResult httpResult; try { - httpResult = mgmApiService.invokeGetAllPlayerProfile(playerId); + httpResult = mgmEsbApiService.invokeGetAllPlayerProfile(playerId); if (httpResult.getStatusCode() != 200) { result.setSuccess(false); @@ -165,7 +165,7 @@ public class PlayerService { HttpResponseResult httpResult; try { - httpResult = mgmApiService.invokeCheckPlayerTPD(playerId, roomCate, dates, bookingType, property); + httpResult = mgmEsbApiService.invokeCheckPlayerTPD(playerId, roomCate, dates, bookingType, property); if (httpResult.getStatusCode() != 200) { result.setSuccess(false); @@ -279,7 +279,7 @@ public class PlayerService { HttpResponseResult httpResult; try { - httpResult = mgmApiService.invokeCheckPlayerSecondTPD(playerId, roomCate, dateRange, bookingType, property); + httpResult = mgmEsbApiService.invokeCheckPlayerSecondTPD(playerId, roomCate, dateRange, bookingType, property); if (httpResult.getStatusCode() != 200) { result.setSuccess(false); result.setMessage("Invoke check player second TPD Failed:[" + httpResult.getStatusCode() + "] " + httpResult.getErrorMessage()); diff --git a/src/main/java/com/mgm/mcrm/player/services/TodoService.java b/src/main/java/com/mgm/mcrm/player/services/TodoService.java index 8b63683..dc17744 100644 --- a/src/main/java/com/mgm/mcrm/player/services/TodoService.java +++ b/src/main/java/com/mgm/mcrm/player/services/TodoService.java @@ -34,7 +34,7 @@ import com.mgm.mcrm.common.dtos.BaseApiResponse; import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; import com.mgm.mcrm.common.dtos.mgm_api.*; import com.mgm.mcrm.common.services.HttpService; -import com.mgm.mcrm.common.services.MGMApiService; +import com.mgm.mcrm.common.services.MGMEsbApiService; import com.mgm.mcrm.common.utils.DateTimeUtil; import com.mgm.mcrm.common.utils.LogUtil; import com.mgm.mcrm.common.utils.SendEmail; @@ -67,7 +67,7 @@ import java.util.concurrent.TimeUnit; public class TodoService { @Resource - private MGMApiService mgmApiService; + private MGMEsbApiService mgmEsbApiService; @Resource private SysSettingService sysSettingService; @@ -129,7 +129,7 @@ public class TodoService { GetDashboardResponse result = new GetDashboardResponse(); try { - HttpResponseResult httpResult = mgmApiService.invokeGetTodoDashboard(userId, 0); + HttpResponseResult httpResult = mgmEsbApiService.invokeGetTodoDashboard(userId, 0); try { TodoDashboardResult resultObj = JSONObject.parseObject(httpResult.getResponseContent(), TodoDashboardResult.class, Feature.SupportNonPublicField, Feature.IgnoreNotMatch); if (Strings.CI.equals(resultObj.getStatusCode(), "OK")) { @@ -159,7 +159,7 @@ public class TodoService { public GetTodoDetailResponse getTodoDetail(String playerId, String todoCategory, String todoSegment, String date) throws Exception { GetTodoDetailResponse result = new GetTodoDetailResponse(); try { - HttpResponseResult httpResult = mgmApiService.invokeGetTodoDetail(playerId, todoCategory, todoSegment, date, 1); + HttpResponseResult httpResult = mgmEsbApiService.invokeGetTodoDetail(playerId, todoCategory, todoSegment, date, 1); try { TodoDetailResult resultObj = JSONObject.parseObject(httpResult.getResponseContent(), TodoDetailResult.class, Feature.SupportNonPublicField, Feature.IgnoreNotMatch); if (Strings.CI.equals(resultObj.getStatusCode(), "OK")) { @@ -185,66 +185,74 @@ public class TodoService { if ("Inhouse".equalsIgnoreCase(todoCategory) || "Arrival".equalsIgnoreCase(todoCategory)) { Bookings bookings = bookingDao.findFirstByConfirmationNoAndPropertyIdIsNotNull(confNum); - CfgProperties property = propertyDao.findById(bookings.getPropertyId()).orElse(null); + if (Objects.nonNull(bookings)) { + CfgProperties property = propertyDao.findById(bookings.getPropertyId()).orElse(null); - if (Objects.nonNull(property)) { + if (Objects.nonNull(property)) { - if ("Inhouse".equalsIgnoreCase(todoCategory)) { - // Parse checkinDate from recordDesc (e.g., "Jun 25 – Jul 1 ...") - LocalDate ciDate = null; - try { - String ciDatePart = recordDesc.split("–")[0].trim(); - // Use the year from the 'date' parameter - int year = Integer.parseInt(date.substring(0, 4)); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM d yyyy", Locale.US); - ciDate = LocalDate.parse(ciDatePart + " " + year, formatter); - } catch (Exception e) { - LogUtil.warn(TodoService.class, "Parse checkinDate error: " + e.getMessage() + ", recordDesc: " + recordDesc); - } - - if (ciDate != null) { - LocalDate today = LocalDate.now(); - if (ciDate.isAfter(today)) { - // 当checkindate大于今天不用拿room number 和room ready - continue; + if ("Inhouse".equalsIgnoreCase(todoCategory)) { + // Parse checkinDate from recordDesc (e.g., "Jun 25 – Jul 1 ...") + LocalDate ciDate = null; + try { + String ciDatePart = recordDesc.split("–")[0].trim(); + // Use the year from the 'date' parameter + int year = Integer.parseInt(date.substring(0, 4)); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM d yyyy", Locale.US); + ciDate = LocalDate.parse(ciDatePart + " " + year, formatter); + } catch (Exception e) { + LogUtil.warn(TodoService.class, "Parse checkinDate error: " + e.getMessage() + ", recordDesc: " + recordDesc); } - BaseApiResponse doorKeyResult = - dlService.getReservationDoorKeyByConfirmationNumber(property.getDlPropertyId(), confNum); - ReservationDoorKeyResponse doorKeyData = doorKeyResult.getData(); - - if (doorKeyResult.isSuccess() && doorKeyData != null) { - String replaceStr = "Conf#" + confNum; - if (StringUtils.isNotBlank(doorKeyData.getRoomNumber())) { - detailRecord.setRoomNumber(doorKeyData.getRoomNumber()); - replaceStr += " [Rm #" + doorKeyData.getRoomNumber() + "]"; + if (ciDate != null) { + LocalDate today = LocalDate.now(); + if (ciDate.isAfter(today)) { + // 当checkindate大于今天不用拿room number 和room ready + continue; } - if (ciDate.isEqual(today)) { - // 当今天的时候要检查room ready - boolean isRoomReady = StringUtils.isNotBlank(doorKeyData.getId()) - && !doorKeyData.isHasKey(); - if (isRoomReady) { - replaceStr += " [Rm Ready]"; + BaseApiResponse doorKeyResult = + dlService.getReservationDoorKeyByConfirmationNumber(property.getDlPropertyId(), confNum); + ReservationDoorKeyResponse doorKeyData = doorKeyResult.getData(); + + if (doorKeyResult.isSuccess() && doorKeyData != null) { + String replaceStr = "Conf#" + confNum; + if (StringUtils.isNotBlank(doorKeyData.getRoomNumber())) { + detailRecord.setRoomNumber(doorKeyData.getRoomNumber()); + replaceStr += " [Rm #" + doorKeyData.getRoomNumber() + "]"; + } + + if (ciDate.isEqual(today)) { + // 当今天的时候要检查room ready + boolean isRoomReady = StringUtils.isNotBlank(doorKeyData.getId()) + && !doorKeyData.isHasKey(); + if (isRoomReady) { + replaceStr += " [Rm Ready]"; + } + } + + if (!replaceStr.equals("Conf#" + confNum)) { + detailRecord.setItemRecordDescription(recordDesc.replace("Conf#" + confNum, replaceStr)); } } - - if (!replaceStr.equals("Conf#" + confNum)) { - detailRecord.setItemRecordDescription(recordDesc.replace("Conf#" + confNum, replaceStr)); - } + } + }else{ + BaseApiResponse queueResult = + dlService.getReservationQueueByConfirmationNumber(property.getDlPropertyId(), confNum); + ReservationQueueResponse queueData = queueResult.getData(); + boolean isQueue = queueResult.isSuccess() + && queueData != null + && queueData.isQueue(); + if (isQueue) { + detailRecord.setItemRecordDescription(recordDesc.replace("Conf#" + confNum, "Conf#" + confNum + " [In Queue]")); } } - }else{ - BaseApiResponse queueResult = - dlService.getReservationQueueByConfirmationNumber(property.getDlPropertyId(), confNum); - ReservationQueueResponse queueData = queueResult.getData(); - boolean isQueue = queueResult.isSuccess() - && queueData != null - && queueData.isQueue(); - if (isQueue) { - detailRecord.setItemRecordDescription(recordDesc.replace("Conf#" + confNum, "Conf#" + confNum + " [In Queue]")); - } } + } else { +// result.setSuccess(false); +// result.setErrorCode("BOOKING NOT FOUND"); +// result.setErrorMessage("BOOKING NOT FOUND"); +// LogUtil.warn(TodoService.class, "[GetTodoDetail] BOOKING NOT FOUND"); +// return result; } } } @@ -475,7 +483,7 @@ public class TodoService { HttpResponseResult httpResult; try { // TODO 这里现在写死只拿daylight的todolist,后面会要拿到Opera的todoList,这个时候要改成两个都拿 by Mauve - httpResult = mgmApiService.invokeGetTodoList(userId, category, type, startDate, endDate, 1); + httpResult = mgmEsbApiService.invokeGetTodoList(userId, category, type, startDate, endDate, 1); } catch (Exception err) { result.setSuccess(false); result.setCode("INVOKE_TODOLISTDETAIL_API_ERROR"); @@ -534,31 +542,34 @@ public class TodoService { if (StringUtils.isNotBlank(confNum)) { if ("Inhouse".equalsIgnoreCase(category) || "Arrival".equalsIgnoreCase(category)) { - List daylightProperties = propertyDao.findByCategory("DAYLIGHT"); - if (CollectionUtils.isNotEmpty(daylightProperties)) { - CfgProperties property = daylightProperties.get(0); - if ("Inhouse".equalsIgnoreCase(category)) { - BaseApiResponse doorKeyResult = - dlService.getReservationDoorKeyByConfirmationNumber(property.getDlPropertyId(), confNum); - ReservationDoorKeyResponse doorKeyData = doorKeyResult.getData(); - boolean isRoomReady = doorKeyResult.isSuccess() - && doorKeyData != null - && StringUtils.isNotBlank(doorKeyData.getId()) - && !doorKeyData.isHasKey(); - if (isRoomReady) { - record.setRoomReady(true); - updateTodoRecordDescription(record, confNum, "[Rm Ready]"); - } - } else { - BaseApiResponse queueResult = - dlService.getReservationQueueByConfirmationNumber(property.getDlPropertyId(), confNum); - ReservationQueueResponse queueData = queueResult.getData(); - boolean isQueue = queueResult.isSuccess() - && queueData != null - && queueData.isQueue(); - if (isQueue) { - record.setIsQueue(true); - updateTodoRecordDescription(record, confNum, "[In Queue]"); + Bookings bookings = bookingDao.findFirstByConfirmationNoAndPropertyIdIsNotNull(confNum); + + if (Objects.nonNull(bookings)) { + CfgProperties property = propertyDao.findById(bookings.getPropertyId()).orElse(null); + if (Objects.nonNull(property)) { + if ("Inhouse".equalsIgnoreCase(category)) { + BaseApiResponse doorKeyResult = + dlService.getReservationDoorKeyByConfirmationNumber(property.getDlPropertyId(), confNum); + ReservationDoorKeyResponse doorKeyData = doorKeyResult.getData(); + boolean isRoomReady = doorKeyResult.isSuccess() + && doorKeyData != null + && StringUtils.isNotBlank(doorKeyData.getId()) + && !doorKeyData.isHasKey(); + if (isRoomReady) { + record.setRoomReady(true); + updateTodoRecordDescription(record, confNum, "[Rm Ready]"); + } + } else { + BaseApiResponse queueResult = + dlService.getReservationQueueByConfirmationNumber(property.getDlPropertyId(), confNum); + ReservationQueueResponse queueData = queueResult.getData(); + boolean isQueue = queueResult.isSuccess() + && queueData != null + && queueData.isQueue(); + if (isQueue) { + record.setIsQueue(true); + updateTodoRecordDescription(record, confNum, "[In Queue]"); + } } } } diff --git a/src/main/java/com/mgm/mcrm/roomcalendar/services/RoomCalendarService.java b/src/main/java/com/mgm/mcrm/roomcalendar/services/RoomCalendarService.java index f5115ac..453545a 100644 --- a/src/main/java/com/mgm/mcrm/roomcalendar/services/RoomCalendarService.java +++ b/src/main/java/com/mgm/mcrm/roomcalendar/services/RoomCalendarService.java @@ -7,7 +7,7 @@ import com.mgm.mcrm.basesetting.property.repositories.PropertyDao; import com.mgm.mcrm.basesetting.room.models.CfgRooms; import com.mgm.mcrm.basesetting.room.repositories.RoomDao; import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; -import com.mgm.mcrm.common.services.MGMApiService; +import com.mgm.mcrm.common.services.MGMEsbApiService; import com.mgm.mcrm.roomcalendar.dtos.RoomCalendarQualification; import com.mgm.mcrm.roomcalendar.dtos.response.GetPropertyRoomTypeListResponse; import com.mgm.mcrm.roomcalendar.dtos.response.GetPropertyRoomTypeResponse; @@ -26,7 +26,7 @@ public class RoomCalendarService { private boolean calendarDataMockEnabled; @Resource - private MGMApiService mgmApiService; + private MGMEsbApiService mgmEsbApiService; @Resource private PropertyDao propertyDao; @Resource @@ -79,7 +79,7 @@ public class RoomCalendarService { if (StringUtils.isNotBlank(visibility.operaProperty) && StringUtils.isNotBlank(visibility.operaRoomType)) { try { - HttpResponseResult response = mgmApiService.invokeRequestRoomCalendar( + HttpResponseResult response = mgmEsbApiService.invokeRequestRoomCalendar( playerId, visibility.operaProperty, visibility.operaRoomType, startDate, endDate, 0); if (response.getStatusCode() == 200) { allRecords.addAll(parseRoomCalendarResponse(response.getResponseContent())); @@ -91,7 +91,7 @@ public class RoomCalendarService { if (StringUtils.isNotBlank(visibility.daylightProperty) && StringUtils.isNotBlank(visibility.daylightRoomType)) { try { - HttpResponseResult response = mgmApiService.invokeRequestRoomCalendar( + HttpResponseResult response = mgmEsbApiService.invokeRequestRoomCalendar( playerId, visibility.daylightProperty, visibility.daylightRoomType, startDate, endDate, 1); if (response.getStatusCode() == 200) { allRecords.addAll(parseRoomCalendarResponse(response.getResponseContent())); @@ -103,7 +103,7 @@ public class RoomCalendarService { if (StringUtils.isNotBlank(visibility.otherProperty) && StringUtils.isNotBlank(visibility.otherRoomType)) { try { - HttpResponseResult response = mgmApiService.invokeRequestRoomCalendar( + HttpResponseResult response = mgmEsbApiService.invokeRequestRoomCalendar( playerId, visibility.otherProperty, visibility.otherRoomType, startDate, endDate, 0); if (response.getStatusCode() == 200) { allRecords.addAll(parseRoomCalendarResponse(response.getResponseContent())); diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 3156df8..1513908 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -82,7 +82,7 @@ server: key-store-type: PKCS12 logging: - config: classpath:logback.xml + config: classpath:log4j2-spring.xml springdoc: swagger-ui: path: /swagger-ui.html @@ -104,16 +104,42 @@ mcrm: security: jwt: secret-key: SecretKeyWhichShouldBeLongEnoughToBeSecure67848563 - api-token-expiration: 5 - mobile-session-expiration: 30 + api-token-expiration: 5 # 分钟单位 + mobile-session-expiration: 43200 # 分钟单位 #============== Daylight Service =================== -daylight: - url: http://localhost:9087/api/daylight/proxy - system-id: 3a151ad3-46be-4644-b656-71d3b037b309 - -notification_proxy: - url: http://localhost:9087/api/notification/proxy +api-gateway: + base-url: http://localhost:9087 + request-timeout-ms: 30000 + daylight: + proxy-path: /api/daylight/proxy + system-id: 3a151ad3-46be-4644-b656-71d3b037b309 + app-id: MCRM_APP + auth-code: securItykeyOfCrmapp87895312 + opera: + proxy-path: /api/opera/proxy + app-id: MCRM_APP + auth-code: securItykeyOfCrmapp87895312 + esb-mcrm: + proxy-path: /api/esb/proxy/api/crmapp/v1.0 + app-id: MCRM_APP + auth-code: securItykeyOfCrmapp87895312 + esb-limo: + proxy-path: /api/esb_limo/proxy/api/limo/v1.0 + app-id: MCRM_APP + auth-code: securItykeyOfCrmapp87895312 + limo: + proxy-path: /api/limo/proxy + app-id: MCRM_APP + auth-code: securItykeyOfCrmapp87895312 + main-handle-url: /MCRMAPI/API/MainHandler.ashx + access-account: test100 + access-password: 1234567 + token-expiry-seconds: 60 + notification: + proxy-path: /api/notification/proxy + app-id: MCRM_APP + auth-code: securItykeyOfCrmapp87895312 sso: callback: @@ -169,10 +195,6 @@ calendar: data-mock-enabled: true #==================APP Config=================== -limo_address: http://mmlimodevapp01/MCRMAPI/API/MainHandler.ashx -limo_getAccessToken_Id: test100 -limo_getAccessToken_pwd: 1234567 - notification: recent-hours: 6 diff --git a/src/main/resources/log4j2-spring.xml b/src/main/resources/log4j2-spring.xml new file mode 100644 index 0000000..c43d848 --- /dev/null +++ b/src/main/resources/log4j2-spring.xml @@ -0,0 +1,41 @@ + + + + crm_mobile_runtime/logs + %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml deleted file mode 100644 index 3545290..0000000 --- a/src/main/resources/logback.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] [%-5level] %logger{50} - %msg%n - - - - - - - ${LOG_APP_HOME}/base.%d{yyyy-MM-dd}.log - - - ${LOG_MAX_HISTORY} - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{500} - %msg%n - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/mockup/app/getTodoList_4.json b/src/main/resources/mockup/app/getTodoList_4.json index d22e348..6ede054 100644 --- a/src/main/resources/mockup/app/getTodoList_4.json +++ b/src/main/resources/mockup/app/getTodoList_4.json @@ -6,7 +6,7 @@ "PlayerId": 11010001, "Date": "Jun 24 (Tue)", "ActionDate":"2025-07-24", - "OperaConfirmationNo":"12312312", + "OperaConfirmationNo":"MGM Cotai;002101C", "TierName": "SUPREME 88", "ChineseName": "劉明浩", "EnglishName": "IVAN LAU", @@ -25,7 +25,7 @@ "MA": "Jojo Chan", "Descriptions": [ "Active Prg:N", - "May7 - May8 Macau -GR | Conf#12312312 | Sharer: Y" + "May7 - May8 Macau -GR | Conf#002101C | Sharer: N" ] }, { diff --git a/src/test/java/com/mgm/daylight/EnumBookingStatusTest.java b/src/test/java/com/mgm/daylight/EnumBookingStatusTest.java deleted file mode 100644 index 903095b..0000000 --- a/src/test/java/com/mgm/daylight/EnumBookingStatusTest.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.mgm.daylight; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import org.hibernate.collection.spi.PersistentBag; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -class EnumBookingStatusTest { - - @Test - void test() { - String a = "{\"birthday\":\"2000-01-01\",\"chineseName\":\"劉明浩\",\"codedFlag\":1,\"country\":\"CN\",\"englishName\":\"IVAN LAU\",\"language\":\"E\",\"name\":\"IVAN LAU\",\"nameIds\":[710825],\"passport\":\"123456\",\"phoneType\":\"MOBILE\",\"playerId\":\"11010001\",\"playerPhone\":\"123456\",\"salutation\":\"Mr.\",\"state\":\"MO\",\"statusCode\":\"OK\",\"statusDescription\":\"SUCCESS\",\"tierId\":\"34\",\"tierName\":\"SUPREME 88\",\"userId\":\"\"}"; - - JSONObject bookingJson = JSON.parseObject(a); - - System.out.println(bookingJson.toJSONString()); - } -} diff --git a/src/test/java/com/mgm/daylight/HttpEntityBuilderTest.java b/src/test/java/com/mgm/daylight/HttpEntityBuilderTest.java deleted file mode 100644 index c46b28b..0000000 --- a/src/test/java/com/mgm/daylight/HttpEntityBuilderTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.mgm.daylight; - -import com.mgm.mcrm.common.httpclient.HttpEntityBuilder; -import org.apache.hc.core5.http.NameValuePair; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class HttpEntityBuilderTest { - - - void checkHasParasKeepsDuplicateQueryNames() throws Exception { - List nvps = new ArrayList<>(); - - String url = HttpEntityBuilder.checkHasParas( - "/api-gateway/booking/v1/reservations?pageNumber=1&pageSize=500" - + "&extend=PurchaseElements&extend=Account&extend=CustomFields", - nvps, - "utf-8"); - - assertEquals("/api-gateway/booking/v1/reservations", url); - assertEquals("PurchaseElements", nvps.get(2).getValue()); - assertEquals("Account", nvps.get(3).getValue()); - assertEquals("CustomFields", nvps.get(4).getValue()); - } -} diff --git a/src/test/java/com/mgm/daylight/SmallTest.java b/src/test/java/com/mgm/daylight/SmallTest.java deleted file mode 100644 index 300cfcb..0000000 --- a/src/test/java/com/mgm/daylight/SmallTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.mgm.daylight; - - -import com.mgm.mcrm.CRMMobileApplication; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest(classes = {CRMMobileApplication.class}) -class SmallTest { - - @Value("${daylight.system-id}") - private String daylightSystemId; - -// @Test - void checkTest() { - System.out.println(daylightSystemId); - } -} diff --git a/src/test/java/com/mgm/mcrm/PublicEndpointsMigrationTest.java b/src/test/java/com/mgm/mcrm/PublicEndpointsMigrationTest.java deleted file mode 100644 index e459b2e..0000000 --- a/src/test/java/com/mgm/mcrm/PublicEndpointsMigrationTest.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.mgm.mcrm; - -import com.auth0.jwt.JWT; -import com.auth0.jwt.algorithms.Algorithm; -import com.mgm.mcrm.appsetting.controllers.CfgSettingController; -import com.mgm.mcrm.appsetting.dtos.CfgSettingQueryRequest; -import com.mgm.mcrm.appsetting.models.CfgSettings; -import com.mgm.mcrm.appsetting.models.SysSetting; -import com.mgm.mcrm.appsetting.services.SettingService; -import com.mgm.mcrm.appsetting.services.SysSettingService; -import com.mgm.mcrm.authorization.services.UserTokenService; -import com.mgm.mcrm.common.config.WebFilterConfig; -import com.mgm.mcrm.logging.controllers.SysLogAppController; -import com.mgm.mcrm.logging.dtos.AppLogDto; -import com.mgm.mcrm.logging.models.SysLogApp; -import com.mgm.mcrm.logging.services.SysLogAppService; -import org.junit.jupiter.api.Test; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; - -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -class PublicEndpointsMigrationTest { - @Test void publicConfigurationRejectsOtherKeysBeforeQuery() throws Exception { - var service = mock(SettingService.class); - var controller = new CfgSettingController(); - ReflectionTestUtils.setField(controller, "settingService", service); - for (String key : new String[]{null, "", "MACAU_IP", "macau_string", "MACAU_STRING "}) { - var query = new CfgSettingQueryRequest(); query.setKeyName(key); - var result = controller.getPublicSettingByKey(new MockHttpServletRequest(), query).getBody(); - assertFalse(result.isSuccess()); assertEquals("SETTING_KEY_NOT_ALLOWED", result.getCode()); - } - assertFalse(controller.getPublicSettingByKey(new MockHttpServletRequest(), null).getBody().isSuccess()); - verifyNoInteractions(service); - var setting = new CfgSettings(); - when(service.getSettingByKey("MACAU_STRING")).thenReturn(setting); - var query = new CfgSettingQueryRequest(); query.setKeyName("MACAU_STRING"); - assertSame(setting, controller.getPublicSettingByKey(new MockHttpServletRequest(), query).getBody().getData()); - verify(service).getSettingByKey("MACAU_STRING"); - } - - @Test void publicLoginSettingsOnlyFetchTwoAllowedKeysAndAllowMissingSettings() { - var service = mock(SysSettingService.class); - var controller = new CfgSettingController(); - ReflectionTestUtils.setField(controller, "sysSettingService", service); - var sso = new SysSetting(); var tpd = new SysSetting(); - when(service.getSysSettingByKey("APP_MOCK_SSO")).thenReturn(sso); - when(service.getSysSettingByKey("APP_MOCK_TPD")).thenReturn(tpd); - assertEquals(java.util.List.of(sso, tpd), controller.getPublicLoginSettings(new MockHttpServletRequest()).getBody().getData()); - verify(service).getSysSettingByKey("APP_MOCK_SSO"); - verify(service).getSysSettingByKey("APP_MOCK_TPD"); - verifyNoMoreInteractions(service); - when(service.getSysSettingByKey("APP_MOCK_TPD")).thenReturn(null); - assertEquals(1, controller.getPublicLoginSettings(new MockHttpServletRequest()).getBody().getData().size()); - } - - @Test void filterAllowsOnlyNewPublicRoutesAndKeepsRefreshProtected() throws Exception { - var config = new WebFilterConfig(); - var tokens = mock(UserTokenService.class); - ReflectionTestUtils.setField(config, "secretKey", "test-only-key"); - ReflectionTestUtils.setField(config, "userTokenService", tokens); - var filter = config.jwtAuthFilter().getFilter(); - for (String route : new String[]{"/system/write_public_log", "/system/sys_setting/get_public_login_settings", "/web/setting/get_public_setting_by_key"}) { - var reached = new AtomicBoolean(); - filter.doFilter(new MockHttpServletRequest("POST", "/api" + route), new MockHttpServletResponse(), - (req, res) -> reached.set(true)); - assertTrue(reached.get(), route); - } - for (String route : new String[]{"/system/write_log", "/system/sys_setting/get_list", "/web/setting/get_setting_by_key", "/app/booking/mock_cancel", "/app/refresh_token"}) { - var reached = new AtomicBoolean(); var response = new MockHttpServletResponse(); - filter.doFilter(new MockHttpServletRequest("POST", "/api" + route), response, (req, res) -> reached.set(true)); - assertFalse(reached.get(), route); assertEquals(401, response.getStatus(), route); - } - } - - private SysLogAppController logs(SysLogAppService service) { - var controller = new SysLogAppController(); - ReflectionTestUtils.setField(controller, "logService", service); - when(service.save(any(SysLogApp.class))).thenAnswer(call -> call.getArgument(0)); - return controller; - } - - @Test void anonymousIdentityCannotBeSpoofedAndNullBodiesAreSafe() { - var service = mock(SysLogAppService.class); var controller = logs(service); - var dto = new AppLogDto(); dto.setLoginId("admin"); - var result = controller.writePublicAppLog(new MockHttpServletRequest(), dto).getBody().getData(); - assertEquals("ANONYMOUS", result.getLoginId()); assertNull(result.getLogDesc()); - assertNull(result.getRequestBody()); assertNull(result.getResponseBody()); assertEquals(0, result.getTagSuccess()); - assertEquals(400, controller.writePublicAppLog(new MockHttpServletRequest(), null).getStatusCode().value()); - verify(service, times(1)).save(any(SysLogApp.class)); - } - - @Test void authenticatedLogUsesTokenIdentityAndRequiresIdentity() { - var service = mock(SysLogAppService.class); var controller = logs(service); - var dto = new AppLogDto(); dto.setLoginId("forged-user"); - var request = new MockHttpServletRequest(); - assertEquals(401, controller.writeAppLog(request, dto).getStatusCode().value()); - verify(service, never()).save(any()); - // Controller identity test; JWT admission is covered separately by the filter test. - request.addHeader("Authorization", "Bearer " + JWT.create().withSubject("80001") - .withClaim("login_id", "actual-user").sign(Algorithm.HMAC256("test-only-key"))); - assertEquals("actual-user", controller.writeAppLog(request, dto).getBody().getData().getLoginId()); - } - - @Test void logsLimitInputAndEscapeText() { - var controller = logs(mock(SysLogAppService.class)); - var dto = new AppLogDto(); dto.setEventCode("e".repeat(101)); dto.setTransId("t".repeat(101)); - dto.setLogDesc("x".repeat(999) + "&"); - var log = controller.writePublicAppLog(new MockHttpServletRequest(), dto).getBody().getData(); - assertEquals(100, log.getEventCode().length()); assertEquals(100, log.getTransId().length()); - assertEquals("x".repeat(999) + "<", log.getLogDesc()); - assertEquals(4000, log.getRequestBody().length()); assertEquals("<script>&", log.getResponseBody()); - } - - @Test void httpRejectsMissingAndMalformedLogBodiesWithoutSaving() throws Exception { - var service = mock(SysLogAppService.class); var mvc = MockMvcBuilders.standaloneSetup(logs(service)).build(); - mvc.perform(post("/api/system/write_public_log")).andExpect(status().isBadRequest()); - mvc.perform(post("/api/system/write_public_log").contentType("application/json") - .content("{\"tagSuccess\":\"not-a-number\"}")).andExpect(status().isBadRequest()); - verify(service, never()).save(any()); - } -} diff --git a/src/test/java/com/mgm/mcrm/RefreshTokenMigrationTest.java b/src/test/java/com/mgm/mcrm/RefreshTokenMigrationTest.java deleted file mode 100644 index 6df496b..0000000 --- a/src/test/java/com/mgm/mcrm/RefreshTokenMigrationTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.mgm.mcrm; - -import com.auth0.jwt.JWT; -import com.auth0.jwt.algorithms.Algorithm; -import com.mgm.mcrm.authorization.dtos.LoginUserInfo; -import com.mgm.mcrm.authorization.filters.JwtAuthFilter; -import com.mgm.mcrm.authorization.models.Token; -import com.mgm.mcrm.authorization.repositories.TokenDao; -import com.mgm.mcrm.authorization.services.UserTokenService; -import com.mgm.mcrm.authorization.utils.JwtTokenUtil; -import com.mgm.mcrm.common.utils.EncryptUtil; -import org.apache.logging.log4j.util.Base64Util; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.test.util.ReflectionTestUtils; - -import java.time.Instant; -import java.time.LocalDateTime; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -class RefreshTokenMigrationTest { - final Map records = new HashMap<>(); - TokenDao dao; UserTokenService service; LoginUserInfo user; - @BeforeEach void setup() { - JwtTokenUtil.setSecretKey("refresh-test-key"); - ReflectionTestUtils.setField(JwtTokenUtil.class, "verifier", null); - JwtTokenUtil.setAccessTokenExpiration(5); JwtTokenUtil.setAppSessionExpiration(30); - dao = mock(TokenDao.class); service = new UserTokenService(dao); - when(dao.save(any(Token.class))).thenAnswer(c -> {Token t=c.getArgument(0); records.put(t.getUuid(),t); return t;}); - when(dao.findByUuid(anyString())).thenAnswer(c -> records.get(c.getArgument(0))); - when(dao.findByUuidAndTokenType(anyString(),eq(1))).thenAnswer(c -> records.get(c.getArgument(0))); - when(dao.deleteByUuidAndTokenType(anyString(),eq(1))).thenAnswer(c -> records.remove(c.getArgument(0)) == null ? 0 : 1); - when(dao.deleteByUserIdAndTokenType(anyString(),eq(1))).thenAnswer(c -> { - int size=records.size(); records.values().removeIf(t -> c.getArgument(0).equals(t.getCreatedBy())); return size-records.size(); - }); - user = new LoginUserInfo(); user.setLoginId("80001"); user.setGroupName("IM"); - } - Token issue(boolean refresh) {return service.saveToken(JwtTokenUtil.generateTokenByLoginUser("/api/app/sso/login",user,1,refresh));} - - @Test void validRefreshPreservesProtocolOwnerGroupAndOriginalRefreshWhenNotRenewing() { - Token old=issue(true); - var result=service.refreshMobileToken(old.getToken(),"/api/app/refresh_token",false); - assertTrue(result.isSuccess()); assertNotNull(result.getAccessToken()); assertNull(result.getRefreshToken()); - assertNotNull(service.validateMobileRefreshToken(old.getToken())); - Token access=service.getToken(result.getAccessToken()); assertEquals("80001",access.getCreatedBy()); - assertEquals("access",JWT.decode(access.getToken()).getClaim("token_use").asString()); - assertEquals("IM",JwtTokenUtil.parseUserFromToken(access.getToken()).getGroupName()); - } - @Test void rotationRevokesOldRefreshAndLogoutRevokesAllOwnedCredentials() { - Token old=issue(true); - var result=service.refreshMobileToken(old.getToken(),"/api/app/refresh_token",true); - assertTrue(result.isSuccess()); assertNull(service.validateMobileRefreshToken(old.getToken())); - assertNotNull(service.validateMobileRefreshToken(result.getRefreshToken())); - assertEquals(2,service.clearTokenByUser("80001",1)); - assertFalse(service.refreshMobileToken(result.getRefreshToken(),"/api/app/refresh_token",false).isSuccess()); - } - @Test void invalidRevokedExpiredAndAccessCredentialsNeverIssueTokens() { - Token refresh=issue(true); Token access=issue(false); int count=records.size(); - for(String value:new String[]{"", "bad", access.getToken(), refresh.getToken()+"x"}) - assertFalse(service.refreshMobileToken(value,"/api/app/refresh_token",false).isSuccess()); - assertEquals(count,records.size()); - refresh.setExpireTime(LocalDateTime.now().minusSeconds(1)); - assertNull(service.validateMobileRefreshToken(refresh.getToken())); - records.clear(); assertNull(service.validateMobileRefreshToken(refresh.getToken())); - } - @Test void legacyWrongPurposeWrongTypeAndExpiredSignedTokensRejected() { - for(String purpose:new String[]{null,"access","refresh"}) { - for(int type:new int[]{0,1}) { - String id=UUID.randomUUID().toString(); - String value=JWT.create().withSubject("80001").withJWTId(id).withClaim("login_id","80001") - .withClaim("token_use",purpose).withClaim("token_type",type) - .withExpiresAt("refresh".equals(purpose) && type == 1 ? Instant.now().minusSeconds(10) : Instant.now().plusSeconds(300)) - .sign(Algorithm.HMAC256("refresh-test-key")); - Token row=new Token(); row.setUuid(id); row.setToken(value); row.setCreatedBy("80001"); - row.setExpireTime(LocalDateTime.now().plusMinutes(30)); records.put(id,row); - assertNull(service.validateMobileRefreshToken(value)); - } - } - Token refresh=issue(true); refresh.setCreatedBy("other-user"); - assertNull(service.validateMobileRefreshToken(refresh.getToken())); - } - @Test void filterUsesRefreshHeaderEvenWithExpiredAccessAndRetainsClientCheck() throws Exception { - Token refresh=issue(true); var filter=new JwtAuthFilter(); filter.setUserTokenService(service); - var request=new MockHttpServletRequest("POST","/api/app/refresh_token"); - request.addHeader("Authorization",JWT.create().withSubject("80001").withExpiresAt(Instant.now().minusSeconds(60)) - .sign(Algorithm.HMAC256("refresh-test-key"))); request.addHeader("RefreshToken",refresh.getToken()); - request.addHeader("clientId",EncryptUtil.sha256(Base64Util.encode("mcrm_80001"))); - var reached=new AtomicBoolean(); var response=new MockHttpServletResponse(); - filter.doFilter(request,response,(req,res)->reached.set(true)); assertTrue(reached.get()); - request.removeHeader("clientId"); reached.set(false); response=new MockHttpServletResponse(); - filter.doFilter(request,response,(req,res)->reached.set(true)); assertFalse(reached.get()); - } - @Test void refreshCannotAuthorizeBusinessRequests() throws Exception { - var filter=new JwtAuthFilter(); filter.setUserTokenService(service); - var request=new MockHttpServletRequest("POST","/api/app/me"); - request.addHeader("Authorization",issue(true).getToken()); - var response=new MockHttpServletResponse(); var reached=new AtomicBoolean(); - filter.doFilter(request,response,(req,res)->reached.set(true)); - assertFalse(reached.get()); assertEquals(401,response.getStatus()); - } -} diff --git a/src/test/java/com/mgm/mcrm/TodoIrcMigrationTest.java b/src/test/java/com/mgm/mcrm/TodoIrcMigrationTest.java deleted file mode 100644 index 0a95602..0000000 --- a/src/test/java/com/mgm/mcrm/TodoIrcMigrationTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.mgm.mcrm; - -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.parser.Feature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.mgm.mcrm.common.dtos.mgm_api.TodoDetailResult; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class TodoIrcMigrationTest { - @Test - void upstreamBalanceSurvivesParsingAndAppSerialization() throws Exception { - for (String amount : new String[]{"0", "1234.50"}) { - TodoDetailResult detail = JSONObject.parseObject( - "{\"IRCProgramFundBalance\":\"" + amount + "\"}", - TodoDetailResult.class, Feature.SupportNonPublicField, Feature.IgnoreNotMatch); - assertEquals(amount, detail.getIrcProgramFundBalance()); - assertEquals(amount, new ObjectMapper().valueToTree(detail).get("iRCProgramFundBalance").asText()); - } - } -} diff --git a/src/test/java/com/mgm/mcrm/TodoNumericMigrationTest.java b/src/test/java/com/mgm/mcrm/TodoNumericMigrationTest.java deleted file mode 100644 index b1f8e64..0000000 --- a/src/test/java/com/mgm/mcrm/TodoNumericMigrationTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.mgm.mcrm; - -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.parser.Feature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.mgm.mcrm.common.dtos.mgm_api.TodoDetailResult; -import com.mgm.mcrm.common.dtos.mgm_api.TodoListRecord; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -class TodoNumericMigrationTest { - private final ObjectMapper mapper = new ObjectMapper(); - private JsonNode detail(String json) { - return mapper.valueToTree(JSONObject.parseObject(json, TodoDetailResult.class, - Feature.SupportNonPublicField, Feature.IgnoreNotMatch)); - } - @Test void numericFieldsPreserveDecimalsLargeNumbersZeroAndNull() { - String[][] fields = {{"PrgTO","prgTO"},{"PrgWin","prgWin"},{"FrontMoney","frontMoney"}, - {"TTO","tto"},{"01CompBalance","comBalance"},{"ExpComm","expComm"}, - {"NetCreditOutstanding","netCreditOutstanding"},{"UnsettledExpenses","unsettledExpenses"}, - {"TodayTo","todayTo"},{"TodayTPD","todayTPD"},{"TodayWin","todayWin"},{"TodayTheo","todayTheo"}, - {"TripWin","tripWin"},{"TripTPD","tripTPD"},{"TripTheo","tripTheo"}, - {"TripRIPercentage","tripRIPercentage"},{"YesterdayTO","yesterdayTO"},{"YesterdayWin","yesterdayWin"}, - {"YesterdayPlayHour","yesterdayPlayHour"},{"AveragePlayHour","averagePlayHour"}}; - for (String[] field : fields) { - for (String value : new String[]{"12.75","3000000000","0","-12.75"}) { - for (String input : new String[]{value, "\"" + value + "\""}) { - JsonNode result = detail("{\"" + field[0] + "\":" + input + "}").get(field[1]); - assertNotNull(result, field[1]); assertTrue(result.isTextual(), field[1]); - assertEquals(value, result.asText(), field[0]); - } - } - assertTrue(detail("{\"" + field[0] + "\":null}").get(field[1]).isNull()); - } - } - @Test void aliasesAndDaylightOnlyDetailFieldsRemain() { - var result = detail("{\"01CompBalance\":\"37.50\",\"TORIPercentage\":0.25,\"HouseAdvantagePercentage\":\"0.1\",\"IRCProgramFundBalance\":\"99\"}"); - assertEquals("37.50", result.get("comBalance").asText()); - assertEquals("37.50", result.get("01CompBalance").asText()); - assertEquals(0.25, result.get("toripercentage").asDouble()); - assertEquals("0.1", result.get("HouseAdvantagePercentage").asText()); - assertEquals("99", result.get("iRCProgramFundBalance").asText()); - } - @Test void listPreservesMaidEtaAndRoomFlagsAlongsideMaId() { - var record = JSONObject.parseObject("{\"MAId\":123,\"ETA\":\"13:45\"}", TodoListRecord.class, - Feature.SupportNonPublicField, Feature.IgnoreNotMatch); - record.setRoomReady(true); record.setIsQueue(true); - JsonNode result = mapper.valueToTree(record); - assertEquals(123,result.get("maid").asInt());assertEquals(123,result.get("maId").asInt()); - assertEquals("13:45",result.get("eta").asText()); - assertTrue(result.get("roomReady").asBoolean());assertTrue(result.get("isQueue").asBoolean()); - assertFalse(result.has("calendarMaId")); - } -} diff --git a/src/test/java/com/mgm/mcrm/TodoRemainingContractTest.java b/src/test/java/com/mgm/mcrm/TodoRemainingContractTest.java deleted file mode 100644 index d3a094e..0000000 --- a/src/test/java/com/mgm/mcrm/TodoRemainingContractTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.mgm.mcrm; - -import com.alibaba.fastjson.JSONObject; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.mgm.mcrm.appsetting.services.SysSettingService; -import com.mgm.mcrm.basesetting.property.repositories.PropertyDao; -import com.mgm.mcrm.booking.models.Bookings; -import com.mgm.mcrm.booking.models.BookingsView; -import com.mgm.mcrm.booking.repositories.BookingDao; -import com.mgm.mcrm.booking.repositories.BookingViewDao; -import com.mgm.mcrm.booking.services.OperaService; -import com.mgm.mcrm.common.dtos.mgm_api.TodoDateListResult; -import com.mgm.mcrm.common.dtos.mgm_api.TodoDetailDescription; -import com.mgm.mcrm.common.dtos.mgm_api.TodoListResult; -import com.mgm.mcrm.player.dtos.request.TodoCancelBookingRequest; -import com.mgm.mcrm.player.services.TodoService; -import org.junit.jupiter.api.Test; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.mockito.Mockito.*; - -class TodoRemainingContractTest { - @Test void remainingDtosPreservePascalInputAndCamelOutput() { - var mapper = new ObjectMapper(); - var list=JSONObject.parseObject("{\"StatusCode\":\"0\",\"StatusDescription\":\"OK\",\"Records\":[{\"MAId\":80001}]}",TodoListResult.class); - var json=mapper.valueToTree(list); - assertEquals("0",json.get("statusCode").asText()); - assertEquals("OK",json.get("statusDescription").asText()); - assertEquals(80001,json.get("records").get(0).get("maId").asInt()); - var dates=JSONObject.parseObject("{\"StatusCode\":\"0\",\"Records\":[{\"Date\":\"2026-09-10\",\"DateRecords\":[{\"MAId\":80001}]}]}",TodoDateListResult.class); - var date=mapper.valueToTree(dates).get("records").get(0); - assertEquals("2026-09-10",date.get("date").asText()); - assertEquals(80001,date.get("dateRecords").get(0).get("maId").asInt()); - var description=JSONObject.parseObject("{\"ItemRecordType\":\"Offer\",\"ItemRecordId\":42,\"ItemRecordDescription\":\"Room\"}",TodoDetailDescription.class); - var detail=mapper.valueToTree(description); - assertEquals("Offer",detail.get("itemRecordType").asText()); - assertEquals(42,detail.get("itemRecordId").asInt()); - assertEquals("Room",detail.get("itemRecordDescription").asText()); - } - - @Test void nullOperaCancellationReturnsFailureInsteadOfThrowing() throws Exception { - var service=new TodoService(); var views=mock(BookingViewDao.class); var bookings=mock(BookingDao.class); - var opera=mock(OperaService.class); var settings=mock(SysSettingService.class); - ReflectionTestUtils.setField(service,"bookingViewDao",views); - ReflectionTestUtils.setField(service,"bookingDao",bookings); - ReflectionTestUtils.setField(service,"operaService",opera); - ReflectionTestUtils.setField(service,"sysSettingService",settings); - ReflectionTestUtils.setField(service,"propertyDao",mock(PropertyDao.class)); - var view=new BookingsView(); view.setPropertyCategory("OPERA"); view.setConfirmationNo("test-confirmation"); - var booking=new Bookings(); - when(views.findById(Long.valueOf(42))).thenReturn(Optional.of(view)); - when(bookings.findById(42L)).thenReturn(Optional.of(booking)); - when(opera.fetchBooking(any())).thenReturn(new JSONObject()); - when(opera.cancelBooking(any())).thenReturn(null); - var request=new TodoCancelBookingRequest(); request.setId(42L); - var result=service.cancelBooking(request,new MockHttpServletRequest()); - assertFalse(result.isSuccess()); assertEquals("SYSTEM_ERROR",result.getCode()); - assertEquals("Cancel Booking Fail",result.getMessage()); - assertEquals("Cancel Booking Fail",booking.getErrorMessage()); - assertEquals("F",booking.getApproveStatus()); - verify(opera,times(1)).cancelBooking(any()); - } -} diff --git a/src/test/java/com/mgm/mcrm/booking/controllers/BookingControllerRecordSearchTest.java b/src/test/java/com/mgm/mcrm/booking/controllers/BookingControllerRecordSearchTest.java deleted file mode 100644 index d2cc0c7..0000000 --- a/src/test/java/com/mgm/mcrm/booking/controllers/BookingControllerRecordSearchTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.mgm.mcrm.booking.controllers; - -import com.mgm.mcrm.appsetting.models.CfgSettings; -import com.mgm.mcrm.appsetting.services.SettingService; -import com.mgm.mcrm.booking.dtos.BookingDto; -import org.junit.jupiter.api.Test; -import org.springframework.test.util.ReflectionTestUtils; - -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -class BookingControllerRecordSearchTest { - - @Test - void filterAndSortBookingResultKeepsPendingVersionWhenSameBookingAppearsTwice() throws Exception { - BookingController controller = new BookingController(); - SettingService settingService = mock(SettingService.class); - ReflectionTestUtils.setField(controller, "settingService", settingService); - - CfgSettings cutoffStart = new CfgSettings(); - cutoffStart.setKeyValues("00:00"); - CfgSettings cutoffEnd = new CfgSettings(); - cutoffEnd.setKeyValues("23:59"); - when(settingService.getSettingByKey("VIEW_YESTERDAY_TODOLIST_START")).thenReturn(cutoffStart); - when(settingService.getSettingByKey("VIEW_YESTERDAY_TODOLIST_END")).thenReturn(cutoffEnd); - - Date futureCheckin = new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000); - Date futureCheckout = new Date(System.currentTimeMillis() + 2 * 24 * 60 * 60 * 1000); - - BookingDto daylightMergedVersion = new BookingDto(); - daylightMergedVersion.setId(100L); - daylightMergedVersion.setConfirmationNo("DL100"); - daylightMergedVersion.setApproveStatus("N"); - daylightMergedVersion.setCheckinDate(futureCheckin); - daylightMergedVersion.setCheckoutDate(futureCheckout); - - BookingDto pendingVersion = new BookingDto(); - pendingVersion.setId(100L); - pendingVersion.setConfirmationNo("DL100"); - pendingVersion.setApproveStatus("PN"); - pendingVersion.setCheckinDate(futureCheckin); - pendingVersion.setCheckoutDate(futureCheckout); - - List input = new ArrayList<>(); - input.add(daylightMergedVersion); - input.add(pendingVersion); - - Method filterMethod = BookingController.class.getDeclaredMethod("filterAndSortBookingResult", List.class); - filterMethod.setAccessible(true); - List result = (List) filterMethod.invoke(controller, input); - - assertEquals(1, result.size()); - } -} diff --git a/src/test/java/com/mgm/mcrm/roomcalendar/CalendarBootstrapMigrationTest.java b/src/test/java/com/mgm/mcrm/roomcalendar/CalendarBootstrapMigrationTest.java deleted file mode 100644 index 3ee92ef..0000000 --- a/src/test/java/com/mgm/mcrm/roomcalendar/CalendarBootstrapMigrationTest.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.mgm.mcrm.roomcalendar; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.mgm.mcrm.appsetting.controllers.CfgSettingController; -import com.mgm.mcrm.appsetting.models.CfgSettings; -import com.mgm.mcrm.appsetting.repositories.SettingDao; -import com.mgm.mcrm.appsetting.services.SettingService; -import com.mgm.mcrm.authorization.models.SysGroupPermission; -import com.mgm.mcrm.authorization.models.SysUserView; -import com.mgm.mcrm.authorization.repositories.SysGroupPermissionDao; -import com.mgm.mcrm.authorization.repositories.SysUserViewDao; -import com.mgm.mcrm.authorization.services.MgmUserService; -import com.mgm.mcrm.authorization.services.UserTokenService; -import com.mgm.mcrm.basesetting.property.repositories.BlockDao; -import com.mgm.mcrm.basesetting.property.repositories.PropertyDao; -import com.mgm.mcrm.basesetting.team.repositories.DepartmentDao; -import com.mgm.mcrm.basesetting.team.repositories.DivisionDao; -import com.mgm.mcrm.basesetting.team.repositories.SectionDao; -import com.mgm.mcrm.basesetting.team.repositories.TeamStructureDao; -import com.mgm.mcrm.common.config.WebFilterConfig; -import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; -import com.mgm.mcrm.common.services.MGMApiService; -import jakarta.servlet.FilterChain; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -@ExtendWith(MockitoExtension.class) -class CalendarBootstrapMigrationTest { - @Mock MGMApiService esb; - @Mock PropertyDao propertyDao; - @Mock BlockDao blockDao; - @Mock DivisionDao divisionDao; - @Mock DepartmentDao departmentDao; - @Mock SectionDao sectionDao; - @Mock TeamStructureDao teamStructureDao; - @Mock SysUserViewDao users; - @Mock SysGroupPermissionDao permissions; - @InjectMocks MgmUserService userService; - - @ParameterizedTest - @ValueSource(strings={"allowed","denied","no-local-user"}) - void calendarPermissionComesFromLocalGroupAndKeepsBookingPermission(String mode) throws Exception { - HttpResponseResult upstream = new HttpResponseResult(); - upstream.setStatusCode(200); - upstream.setResponseContent("{\"StatusCode\":\"OK\",\"Records\":[{\"userId\":\"123\",\"userName\":\"Tester\",\"canViewCalendar\":true}]}"); - when(esb.invokeGetUserInfo("123","")).thenReturn(upstream); - if (!mode.equals("no-local-user")) { - SysUserView user = new SysUserView(); - user.setGroupId(7L); user.setGroupName("test-group"); - when(users.getByLoginId("123")).thenReturn(user); - SysGroupPermission booking = new SysGroupPermission(); - booking.setPermissionId("10001_01"); - SysGroupPermission calendar = new SysGroupPermission(); - calendar.setPermissionId("10001_07"); - when(permissions.getAllByGroupId(7L)).thenReturn( - mode.equals("allowed") ? List.of(booking,calendar) : List.of(booking)); - } - var result = userService.findUserInfoByUserId("123"); - assertTrue(result.isSuccess(),result.getMessage()); - var json = new ObjectMapper().valueToTree(result.getData()); - assertEquals(mode.equals("allowed"),json.get("canViewCalendar").asBoolean()); - assertEquals(!mode.equals("no-local-user"),result.getData().isCanViewBooking()); - } - - @ParameterizedTest - @ValueSource(booleans={true,false}) - void mockSwitchOnlyGrantsCalendarWithoutGroupConfiguration(boolean enabled) throws Exception { - ReflectionTestUtils.setField(userService,"calendarPermissionMockEnabled",enabled); - HttpResponseResult upstream = new HttpResponseResult(); - upstream.setStatusCode(200); - upstream.setResponseContent("{\"StatusCode\":\"OK\",\"Records\":[{\"userId\":\"123\"}]}"); - when(esb.invokeGetUserInfo("123","")).thenReturn(upstream); - var result = userService.findUserInfoByUserId("123"); - assertTrue(result.isSuccess(),result.getMessage()); - assertEquals(enabled,result.getData().isCanViewCalendar()); - assertFalse(result.getData().isCanViewBooking()); - verifyNoInteractions(permissions); - } - - CfgSettings setting(String key,String value) { - CfgSettings setting = new CfgSettings(); - setting.setKeys(key); setting.setKeyValues(value); - return setting; - } - - @Test void calendarSettingsExposeOnlyFiveExactKeysAndPreserveValues() throws Exception { - List keys = List.of("CALENDAR_VIEW_PREVIOUS_DAYS","CALENDAR_VIEW_VISIBLE_DAYS", - "CALENDAR_VIEW_YESTERDAY_START","CALENDAR_VIEW_YESTERDAY_END","MAX_SELECTED_ROOM_TYPES"); - List rows = new ArrayList<>(); - for (String key : keys) rows.add(setting(key,"3")); - rows.add(setting("OTHER_SETTING","private")); - rows.add(setting("calendar_view_previous_days","wrong-case")); - rows.add(setting(null,"missing-key")); - rows.add(null); - SettingDao dao = mock(SettingDao.class); - when(dao.findAll()).thenReturn(rows); - SettingService service = new SettingService(); - ReflectionTestUtils.setField(service,"settingDao",dao); - CfgSettingController controller = new CfgSettingController(); - ReflectionTestUtils.setField(controller,"settingService",service); - var result = controller.getCalendarSetting(); - assertEquals(200,result.getStatusCode().value()); - assertTrue(result.getBody().isSuccess()); - assertEquals(keys,result.getBody().getData().stream().map(CfgSettings::getKeys).toList()); - var json = new ObjectMapper().valueToTree(result.getBody()); - assertEquals("3",json.get("data").get(0).get("keyValues").asText()); - } - - @Test void missingSettingsReturnEmptyListWithoutInventedDefaults() { - SettingDao dao = mock(SettingDao.class); - when(dao.findAll()).thenReturn(List.of()); - SettingService service = new SettingService(); - ReflectionTestUtils.setField(service,"settingDao",dao); - assertTrue(service.getCalendarSetting().isEmpty()); - } - - @Test void calendarSettingsRouteRemainsBehindExistingJwtFilter() throws Exception { - WebFilterConfig config = new WebFilterConfig(); - ReflectionTestUtils.setField(config,"secretKey","calendar-test-only-secret"); - ReflectionTestUtils.setField(config,"userTokenService",mock(UserTokenService.class)); - var filter = config.jwtAuthFilter().getFilter(); - var request = new MockHttpServletRequest("POST","/api/app/setting/get_calendar_setting"); - var response = new MockHttpServletResponse(); - FilterChain chain = mock(FilterChain.class); - filter.doFilter(request,response,chain); - assertEquals(401,response.getStatus()); - verifyNoInteractions(chain); - } -} diff --git a/src/test/java/com/mgm/mcrm/roomcalendar/CalendarMockDataTest.java b/src/test/java/com/mgm/mcrm/roomcalendar/CalendarMockDataTest.java deleted file mode 100644 index 64bce90..0000000 --- a/src/test/java/com/mgm/mcrm/roomcalendar/CalendarMockDataTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.mgm.mcrm.roomcalendar; - -import com.mgm.mcrm.appsetting.repositories.SettingDao; -import com.mgm.mcrm.appsetting.services.SettingService; -import com.mgm.mcrm.basesetting.property.repositories.PropertyDao; -import com.mgm.mcrm.basesetting.room.repositories.RoomDao; -import com.mgm.mcrm.common.services.MGMApiService; -import com.mgm.mcrm.roomcalendar.services.CalendarMockData; -import com.mgm.mcrm.roomcalendar.services.RoomCalendarService; -import org.junit.jupiter.api.Test; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.*; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -class CalendarMockDataTest { - @Test void catalogAndQueriesAgreeForEveryHotelAcrossMonthBoundary() { - var catalog = CalendarMockData.metadata().getPropertyList(); - var hotels = new ArrayList<>(catalog.subList(0,2)); - hotels.addAll(catalog.get(2).getOtherProperties()); - assertEquals(6,hotels.size()); - int count=0; - for (var hotel:hotels) { - - String rooms=String.join(",",hotel.getRoomTypes().stream().map(t->t.getCode()).toList()); - var rows=CalendarMockData.records("",hotel.getCode(),rooms,"2026-09-28","2026-10-03"); - assertEquals(hotel.getRoomTypes().size()*6,rows.size()); - assertTrue(rows.stream().anyMatch(row->row.getDate().equals("2026-10-01"))); - assertEquals(rows,CalendarMockData.records("",hotel.getCode(),rooms,"2026-09-28","2026-10-03")); - count+=rows.size(); - } - assertEquals(96,count); - } - - @Test void variedAvailabilityAndPlayerQualificationAreDeterministic() { - var rows=CalendarMockData.records("80001","SIM_COTAI","CR,ES","2026-09-01","2026-10-31"); - assertTrue(rows.stream().anyMatch(r->r.getRoomCount()==0)); - assertTrue(rows.stream().anyMatch(r->r.getRoomCount()>0)); - assertEquals(Set.of(0,1),new HashSet<>(rows.stream().map(r->r.getBookingDateQualificationFlag()).toList())); - assertNotEquals(rows,CalendarMockData.records("80002","SIM_COTAI","CR,ES","2026-09-01","2026-10-31")); - assertEquals(4,CalendarMockData.records("","SIM_MO,SIM_LB","MO_STD,LB_GRAND","2026-09-01","2026-09-02").size()); - } - - @Test void invalidRequestsFailInsteadOfProducingPlausibleData() { - assertThrows(IllegalArgumentException.class,()->CalendarMockData.records("","MC","CR","2026-09-01","2026-09-02")); - assertThrows(IllegalArgumentException.class,()->CalendarMockData.records("","SIM_COTAI","GR","2026-09-01","2026-09-02")); - assertThrows(IllegalArgumentException.class,()->CalendarMockData.records("","SIM_COTAI","CR","2026-09-02","2026-09-01")); - assertThrows(IllegalArgumentException.class,()->CalendarMockData.records("","SIM_COTAI","CR","2026-01-01","2026-12-31")); - } - - @Test void enabledMocksDoNotReadDatabaseOrCallEsbAndDisabledSettingsUseDatabase() throws Exception { - var service=new RoomCalendarService(); - var properties=mock(PropertyDao.class); var rooms=mock(RoomDao.class); var esb=mock(MGMApiService.class); - ReflectionTestUtils.setField(service,"propertyDao",properties); - ReflectionTestUtils.setField(service,"roomDao",rooms); - ReflectionTestUtils.setField(service,"mgmApiService",esb); - ReflectionTestUtils.setField(service,"calendarDataMockEnabled",true); - assertEquals(3,service.getPropertyRoomType().getPropertyList().size()); - var result=service.getRoomCalendar(Map.of("Property","SIM_COTAI","RoomType","CR", - "StartDate","2026-09-01","EndDate","2026-09-03")); - assertTrue(result.isSuccess()); assertEquals(3,result.getData().size()); - assertEquals("CALENDAR_MOCK_DATA",result.getMessage()); - verifyNoInteractions(properties,rooms,esb); - var settings=new SettingService(); var dao=mock(SettingDao.class); - ReflectionTestUtils.setField(settings,"settingDao",dao); - ReflectionTestUtils.setField(settings,"calendarDataMockEnabled",true); - assertEquals("60",settings.getCalendarSetting().stream() - .filter(s->s.getKeys().equals("CALENDAR_VIEW_VISIBLE_DAYS")).findFirst().orElseThrow().getKeyValues()); - verifyNoInteractions(dao); - ReflectionTestUtils.setField(settings,"calendarDataMockEnabled",false); - when(dao.findAll()).thenReturn(List.of()); - assertTrue(settings.getCalendarSetting().isEmpty()); verify(dao).findAll(); - } -} diff --git a/src/test/java/com/mgm/mcrm/roomcalendar/CalendarQueryMigrationTest.java b/src/test/java/com/mgm/mcrm/roomcalendar/CalendarQueryMigrationTest.java deleted file mode 100644 index dc2a21a..0000000 --- a/src/test/java/com/mgm/mcrm/roomcalendar/CalendarQueryMigrationTest.java +++ /dev/null @@ -1,254 +0,0 @@ -package com.mgm.mcrm.roomcalendar; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.mgm.mcrm.authorization.dtos.LoginUserInfo; -import com.mgm.mcrm.authorization.services.SysGroupPermissionService; -import com.mgm.mcrm.authorization.utils.JwtTokenUtil; -import com.mgm.mcrm.basesetting.property.models.CfgProperties; -import com.mgm.mcrm.basesetting.property.repositories.PropertyDao; -import com.mgm.mcrm.basesetting.room.models.CfgRooms; -import com.mgm.mcrm.basesetting.room.repositories.RoomDao; -import com.mgm.mcrm.common.dtos.http_util.HttpResponseResult; -import com.mgm.mcrm.common.services.MGMApiService; -import com.mgm.mcrm.roomcalendar.controllers.RoomCalendarController; -import com.mgm.mcrm.roomcalendar.repositories.RoomCalendarRoomProjection; -import com.mgm.mcrm.roomcalendar.services.RoomCalendarService; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.util.ReflectionTestUtils; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -class CalendarQueryMigrationTest { - PropertyDao properties; - RoomDao rooms; - MGMApiService esb; - RoomCalendarService service; - - @BeforeEach void setup() { - properties = mock(PropertyDao.class); - rooms = mock(RoomDao.class); - esb = mock(MGMApiService.class); - service = new RoomCalendarService(); - ReflectionTestUtils.setField(service, "propertyDao", properties); - ReflectionTestUtils.setField(service, "roomDao", rooms); - ReflectionTestUtils.setField(service, "mgmApiService", esb); - } - - Map request(boolean pascal) { - Map result = new HashMap<>(); - result.put(pascal ? "Property" : "property", "Macau"); - result.put(pascal ? "RoomType" : "roomType", "SUITE"); - result.put(pascal ? "StartDate" : "startDate", "2026-09-10"); - result.put(pascal ? "EndDate" : "endDate", "2026-09-12"); - return result; - } - - CfgProperties property(long id, String name, String category, int order) { - CfgProperties p = new CfgProperties(); - p.setId(id); p.setCalendarName(name); p.setName(name); - p.setCode("MO"); p.setCategory(category); - p.setCalendarOrder(order); p.setShowCalendar(1); - return p; - } - - void visible() { - when(properties.findByCalendarNameIn(any())).thenReturn(List.of(property(1,"Macau","OPERA",1))); - CfgRooms room = new CfgRooms(); - room.setPropertyId("1"); room.setCalendarName("SUITE"); room.setShowCalendar(1); - when(rooms.findByPropertyIdInAndCalendarNameIn(List.of("1"),List.of("SUITE"))) - .thenReturn(List.of(room)); - } - - HttpResponseResult response(int status, String body) { - HttpResponseResult response = new HttpResponseResult(); - response.setStatusCode(status); response.setResponseContent(body); - return response; - } - - @ParameterizedTest @ValueSource(booleans={true,false}) - void aliasesResolveCalendarNameToEsbCodeAndNormalizeResponse(boolean pascal) throws Exception { - visible(); - when(esb.invokeRequestRoomCalendar("","MO","SUITE","2026-09-10","2026-09-12",0)) - .thenReturn(response(200,"{\"StatusCode\":\"OK\",\"Records\":[{\"Property\":\"MO\",\"RoomType\":\"SUITE\",\"Date\":\"2026-09-10T00:00:00\",\"RoomCriteria\":\"A\",\"RoomCount\":3,\"PropertyLeader\":\"L\",\"BookingDateQualificationFlag\":1}]}")); - var result = service.getRoomCalendar(request(pascal)); - assertTrue(result.isSuccess()); - assertEquals("2026-09-10",result.getData().getFirst().getDate()); - assertEquals(3,result.getData().getFirst().getRoomCount()); - verify(esb).invokeRequestRoomCalendar("","MO","SUITE","2026-09-10","2026-09-12",0); - } - - @Test void missingInputDoesNotCallDatabaseOrEsb() throws Exception { - assertFalse(service.getRoomCalendar(Map.of()).isSuccess()); - verifyNoInteractions(properties,rooms,esb); - } - - @Test void hiddenPropertyDoesNotReachRoomLookupOrEsb() throws Exception { - CfgProperties p = property(1,"Macau","OPERA",1); p.setShowCalendar(0); - when(properties.findByCalendarNameIn(any())).thenReturn(List.of(p)); - var result = service.getRoomCalendar(request(true)); - assertEquals("PROPERTYNOSHOW",result.getErrorCode()); - verifyNoInteractions(rooms,esb); - } - - @Test void hiddenRoomDoesNotReachEsb() throws Exception { - visible(); - CfgRooms hidden = new CfgRooms(); hidden.setCalendarName("SUITE"); hidden.setShowCalendar(0); - when(rooms.findByPropertyIdInAndCalendarNameIn(anyList(),anyList())).thenReturn(List.of(hidden)); - assertEquals("ROOMTYPENOSHOW",service.getRoomCalendar(request(true)).getErrorCode()); - verifyNoInteractions(esb); - } - - @ParameterizedTest @ValueSource(ints={200,503}) - void upstreamFailureIsNotEmptySuccess(int status) throws Exception { - visible(); - when(esb.invokeRequestRoomCalendar(anyString(),anyString(),anyString(),anyString(),anyString(),anyInt())) - .thenReturn(response(status,"{\"StatusCode\":\"FAIL\",\"StatusDescription\":\"Rejected\"}")); - assertFalse(service.getRoomCalendar(request(true)).isSuccess()); - } - - RoomCalendarRoomProjection projected(String propertyId,String name,int order) { - var row = mock(RoomCalendarRoomProjection.class); - when(row.getPropertyId()).thenReturn(propertyId); - when(row.getCalendarName()).thenReturn(name); - when(row.getCalendarOrder()).thenReturn(order); - when(row.getDefaultShowCalendar()).thenReturn(1); - return row; - } - - @ParameterizedTest @ValueSource(strings={"OPERA","DAYLIGHT"}) - void metadataOrdersRoomsCapsDefaultsAndGroupsOtherProperties(String category) { - CfgProperties hidden = property(3,"Hidden",category,0); hidden.setShowCalendar(0); - when(properties.findAll()).thenReturn(new ArrayList<>(List.of( - property(2,"Other Hotel","OTHER",2),hidden,property(1,"Macau",category,1)))); - List projectedRooms = List.of( - projected("1","D",4), projected("1","B",2), projected("1","A",1), - projected("1","C",3), projected("2","X",1)); - when(rooms.queryCalendarRoomsByPropertyIds(anyList())).thenReturn(projectedRooms); - var result = service.getPropertyRoomType(); - assertTrue(result.isSuccess()); - assertEquals(2,result.getPropertyList().size()); - var macau = result.getPropertyList().getFirst(); - assertEquals("Macau",macau.getCode()); - assertEquals(List.of("A","B","C"),macau.getDefaultRoomTypes()); - var other = result.getPropertyList().get(1); - assertEquals("OTHER",other.getCode()); - assertEquals(List.of("Other Hotel"),other.getDefaultRoomTypes()); - assertEquals("X",other.getOtherProperties().getFirst().getRoomTypes().getFirst().getCode()); - verifyNoInteractions(esb); - } - - @Test void daylightCalendarUsesConfiguredPropertyCodeAndRoomCalendarName() throws Exception { - CfgProperties daylight = property(1090,"Cotai","DAYLIGHT",1); - daylight.setCode("configured-daylight-code"); - when(properties.findByCalendarNameIn(List.of("Cotai"))).thenReturn(List.of(daylight)); - CfgRooms room = new CfgRooms(); - room.setPropertyId("1090"); room.setCalendarName("configured-room-name"); room.setShowCalendar(1); - when(rooms.findByPropertyIdInAndCalendarNameIn(List.of("1090"),List.of("configured-room-name"))) - .thenReturn(List.of(room)); - when(esb.invokeRequestRoomCalendar("","configured-daylight-code","configured-room-name","2026-09-10","2026-09-12",1)) - .thenReturn(response(200,"{\"StatusCode\":\"OK\",\"Records\":[]}")); - var payload = request(true); - payload.put("Property","Cotai"); payload.put("RoomType","configured-room-name"); - assertTrue(service.getRoomCalendar(payload).isSuccess()); - verify(esb).invokeRequestRoomCalendar("","configured-daylight-code","configured-room-name","2026-09-10","2026-09-12",1); - } - - @ParameterizedTest @ValueSource(strings={"","12345"}) - void esbAdapterUsesAuthenticatedPostAndOptionalPlayer(String player) throws Exception { - MGMApiService adapter = spy(new MGMApiService()); - doReturn("test-token").when(adapter).getAccessToken(); - HttpResponseResult expected = response(200,"{}"); - doAnswer(call -> { - assertEquals("/RequestRoomCalendar",call.getArgument(1)); - com.alibaba.fastjson.JSONObject body = call.getArgument(2); - assertEquals("MO",body.getString("Property")); - assertEquals("SUITE",body.getString("RoomType")); - assertEquals("2026-09-10",body.getString("StartDate")); - assertEquals("2026-09-12",body.getString("EndDate")); - assertEquals(player.isEmpty() ? null : player,body.getString("PlayerId")); - assertEquals("POST",call.getArgument(3)); - assertEquals(Map.of("Authorization","test-token"),call.getArgument(4)); - return expected; - }).when(adapter).invokeESBAPI(anyString(),anyString(),any(),anyString(),anyMap(),anyInt()); - assertSame(expected,adapter.invokeRequestRoomCalendar(player,"MO","SUITE","2026-09-10","2026-09-12")); - } - - @Test void permittedPlayerRequestReachesServiceWithoutLosingPayload() throws Exception { - RoomCalendarController controller = new RoomCalendarController(); - RoomCalendarService queries = mock(RoomCalendarService.class); - SysGroupPermissionService permissions = mock(SysGroupPermissionService.class); - ReflectionTestUtils.setField(controller,"roomCalendarService",queries); - ReflectionTestUtils.setField(controller,"sysGroupPermissionService",permissions); - MockHttpServletRequest req = new MockHttpServletRequest(); - LoginUserInfo user = new LoginUserInfo(); user.setGroupName("calendar-users"); - when(permissions.checkGroupPermission("calendar-users","10001_07")).thenReturn(true); - Map payload = request(true); payload.put("PlayerId","12345"); - var expected = new com.mgm.mcrm.roomcalendar.dtos.response.RoomCalendarApiResponse>(); - expected.setSuccess(true); - when(queries.getRoomCalendar(payload)).thenReturn(expected); - try (var jwt = mockStatic(JwtTokenUtil.class)) { - jwt.when(() -> JwtTokenUtil.parseUserFromRequest(req)).thenReturn(user); - assertSame(expected,controller.getPlayerRoomQualification(req,payload)); - verify(queries).getRoomCalendar(payload); - } - } - - @ParameterizedTest @ValueSource(booleans={true,false}) - void mockPermissionAllowsAllCalendarRoutesButStillRequiresUser(boolean loggedIn) throws Exception { - RoomCalendarController controller = new RoomCalendarController(); - RoomCalendarService queries = mock(RoomCalendarService.class); - SysGroupPermissionService permissions = mock(SysGroupPermissionService.class); - ReflectionTestUtils.setField(controller,"roomCalendarService",queries); - ReflectionTestUtils.setField(controller,"sysGroupPermissionService",permissions); - ReflectionTestUtils.setField(controller,"calendarPermissionMockEnabled",true); - MockHttpServletRequest req = new MockHttpServletRequest(); - try (var jwt = mockStatic(JwtTokenUtil.class)) { - jwt.when(() -> JwtTokenUtil.parseUserFromRequest(req)) - .thenReturn(loggedIn ? new LoginUserInfo() : null); - var general = controller.requestRoomCalendar(req,request(true)); - var player = controller.getPlayerRoomQualification(req,request(true)); - var metadata = controller.requestPropertiesRoomTypes(req); - if (loggedIn) { - verify(queries,times(2)).getRoomCalendar(request(true)); - verify(queries).getPropertyRoomType(); - } else { - assertEquals("NO_PERMISSION",general.getErrorCode()); - assertEquals("NO_PERMISSION",player.getErrorCode()); - assertEquals("NO_PERMISSION",metadata.getErrorCode()); - verifyNoInteractions(queries); - } - verifyNoInteractions(permissions); - } - } - - @Test void allControllerRoutesRejectDeniedGroupAndSerializeCompatibleErrors() throws Exception { - RoomCalendarController controller = new RoomCalendarController(); - RoomCalendarService queries = mock(RoomCalendarService.class); - SysGroupPermissionService permissions = mock(SysGroupPermissionService.class); - ReflectionTestUtils.setField(controller,"roomCalendarService",queries); - ReflectionTestUtils.setField(controller,"sysGroupPermissionService",permissions); - MockHttpServletRequest req = new MockHttpServletRequest(); - LoginUserInfo user = new LoginUserInfo(); user.setGroupName("restricted"); - try (var jwt = mockStatic(JwtTokenUtil.class)) { - jwt.when(() -> JwtTokenUtil.parseUserFromRequest(req)).thenReturn(user); - var result = controller.requestRoomCalendar(req,request(true)); - var json = new ObjectMapper().valueToTree(result); - assertEquals("NO_PERMISSION",json.get("code").asText()); - assertEquals("NO_PERMISSION",json.get("errorCode").asText()); - assertEquals("NO_PERMISSION",controller.getPlayerRoomQualification(req,request(true)).getErrorCode()); - assertEquals("NO_PERMISSION",controller.requestPropertiesRoomTypes(req).getErrorCode()); - verify(permissions,times(3)).checkGroupPermission("restricted","10001_07"); - verifyNoInteractions(queries); - } - } -} diff --git a/target/crm_mobile_runtime/appconfig.properties b/target/crm_mobile_runtime/appconfig.properties deleted file mode 100644 index 51acda8..0000000 --- a/target/crm_mobile_runtime/appconfig.properties +++ /dev/null @@ -1,16 +0,0 @@ -#ESB Setting -esb_app_id=mgmtestuser001 -esb_app_secret=MGM@!666 -esb_server=https://mmesbdevapp14/api/crmapp/v1.0 -esb_request_timeout=30000 - -#OWS Setting -ows_server=https://mgpmsdevows45 -ows_request_timeout=30000 - -#SMTP setting -mail_port=25 -mail_smtp_host=smtp.aliyun.com -mail_sender=linp168@aliyun.com -mail_sender_password=linpeng060504 -mail_send_to=1441170195@qq.com;feng.cai@willtechmacau.com \ No newline at end of file diff --git a/target/crm_mobile_runtime/excel_define/import/import_other_booking.json b/target/crm_mobile_runtime/excel_define/import/import_other_booking.json deleted file mode 100644 index c8805e6..0000000 --- a/target/crm_mobile_runtime/excel_define/import/import_other_booking.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "importName": "IMPORT_OTHER_BOOKING", - "importPages": [{ - "sheetIndx": 0, - "dataElements": [{ - "offsetRow": 1, - "offsetCol": 0, - "objClassName": "com.mgm.mcrm.common.dtos.data_import.booking.OtherBookingImportRow", - "columnDefs": [ - { - "columnHeader":"#", - "checkHeader":false, - "headerRowIdx":0 - }, - { - "columnHeader":"Staff Action", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "otherFollowUpStatusName", - "dataType": "String" - }, - { - "columnHeader":"Room Status", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "otherRoomStatusName", - "dataType": "String" - }, - { - "columnHeader":"Property", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "propertyName", - "dataType": "String" - }, - { - "columnHeader":"Confirmation No.", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "confirmationNo", - "dataType": "String" - }, - { - "columnHeader":"Room Type", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "roomName", - "dataType": "String" - }, - { - "columnHeader":"Check In Date", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "checkinDate", - "dataType": "String" - }, - { - "columnHeader":"Check Out Date", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "checkoutDate", - "dataType": "String" - }, - { - "columnHeader":"Change Check In Date", - "checkHeader":false, - "headerRowIdx":0 - }, - { - "columnHeader":"Change Check Out Date", - "checkHeader":false, - "headerRowIdx":0 - }, - { - "columnHeader":"Requested C/O Date", - "checkHeader":false, - "headerRowIdx":0 - }, - { - "columnHeader":"Player ID", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "playerId", - "dataType": "String" - }, - { - "columnHeader":"Title", - "checkHeader":false, - "headerRowIdx":0 - }, - { - "columnHeader":"Last Name", - "checkHeader":false, - "headerRowIdx":0 - }, - { - "columnHeader":"First Name", - "checkHeader":false, - "headerRowIdx":0, - "attrName": "playerName", - "dataType": "String" - }, - { - "columnHeader":"Tier", - "checkHeader":false, - "headerRowIdx":0 - }, - { - "columnHeader":"Market Code", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "marketCodeName", - "dataType": "String" - }, - { - "columnHeader":"Source Code", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "sourceCodeName", - "dataType": "String" - }, - { - "columnHeader":"Booker", - "checkHeader":false, - "headerRowIdx":0 - }, - { - "columnHeader":"Booker Id", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "bookerId", - "dataType": "String" - }, - { - "columnHeader":"Sharer", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "sharer", - "dataType": "String" - }, - { - "columnHeader":"Sharer Name", - "checkHeader":false, - "headerRowIdx":0 - }, - { - "columnHeader":"Remark", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "remark", - "dataType": "String" - }, - { - "columnHeader":"Approver", - "checkHeader":false, - "headerRowIdx":0 - }, - { - "columnHeader":"Approver ID", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "approverId", - "dataType": "String" - }, - { - "columnHeader":"Approval Remark", - "checkHeader":false, - "headerRowIdx":0 - }, - { - "columnHeader":"Booking Type", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "bookingTypeName", - "dataType": "String" - }, - { - "columnHeader":"Segment", - "checkHeader":true, - "headerRowIdx":0, - "attrName": "segmentName", - "dataType": "String" - }, - { - "columnHeader":"ETA Time", - "checkHeader":false, - "headerRowIdx":0 - }, - { - "columnHeader":"Create Date/Time", - "checkHeader":false, - "headerRowIdx":0 - } - ] - }] - }] -}