commit cc4739711cc4a442ab4d2bbbd1d1aad3d64a7e81 Author: Mauve Date: Tue Sep 8 17:12:13 2026 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2db5a77 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/crm_runtime/logs/ +/target/ +/.idea/ +/.vscode/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..38b08b6 --- /dev/null +++ b/pom.xml @@ -0,0 +1,165 @@ + + 4.0.0 + + mgm + MCRMApiGateway + 1.0.1-SNAPSHOT + jar + + MCRMApiGateway + + + org.springframework.boot + spring-boot-starter-parent + 3.5.7 + + + + + UTF-8 + 21 + ${java.version} + ${java.version} + + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-webflux + + + org.springframework.boot + spring-boot-starter-mail + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-starter-activemq + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot + spring-boot-starter-data-jpa + + + com.microsoft.sqlserver + mssql-jdbc + + + org.apache.httpcomponents.client5 + httpclient5 + 5.5 + + + org.springframework.boot + spring-boot-starter-tomcat + + + com.alibaba + fastjson + 2.0.50 + + + org.apache.commons + commons-lang3 + 3.18.0 + + + org.apache.commons + commons-text + 1.14.0 + + + org.springframework.boot + spring-boot-devtools + true + + + org.projectlombok + lombok + provided + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.8.11 + + + org.mapstruct + mapstruct + 1.6.3 + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + org.mapstruct + mapstruct-processor + 1.6.3 + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + com.mgm.mcrm.apigateway.CRMApiGatewayApplication + + + + + + repackage + + + + + + maven-compiler-plugin + + ${java.version} + ${java.version} + UTF-8 + + + + org.codehaus.mojo + license-maven-plugin + 2.7.0 + + + EPL-1.0 + LGPL-2.1 + + + + + + + MCRM API Gateway + diff --git a/src/main/java/com/mgm/mcrm/apigateway/CRMApiGatewayApplication.java b/src/main/java/com/mgm/mcrm/apigateway/CRMApiGatewayApplication.java new file mode 100644 index 0000000..9d07272 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/CRMApiGatewayApplication.java @@ -0,0 +1,27 @@ +package com.mgm.mcrm.apigateway; + +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.context.annotation.ComponentScan; +import org.springframework.jms.annotation.EnableJms; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.transaction.annotation.EnableTransactionManagement; + + +@SpringBootApplication +@EntityScan(basePackages="com.mgm.mcrm.apigateway") +@ComponentScan(basePackages="com.mgm.mcrm.apigateway") +@EnableTransactionManagement +@ServletComponentScan +@EnableScheduling +@EnableAsync +@EnableJms +public class CRMApiGatewayApplication { + + public static void main( String[] args ) { + SpringApplication.run(CRMApiGatewayApplication.class, args); + } +} \ No newline at end of file diff --git a/src/main/java/com/mgm/mcrm/apigateway/allotment/dtos/request/BlockCodeChannelDto.java b/src/main/java/com/mgm/mcrm/apigateway/allotment/dtos/request/BlockCodeChannelDto.java new file mode 100644 index 0000000..f06639a --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/allotment/dtos/request/BlockCodeChannelDto.java @@ -0,0 +1,19 @@ +package com.mgm.mcrm.apigateway.allotment.dtos.request; + +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class BlockCodeChannelDto { + private Long blockCodeId; + private String code; + private String category; + private LocalDateTime startDate; + private LocalDateTime endDate; + private String sourceName; + private String dlBlockId; + private Integer channelTotal; + private Integer available; + private Integer pickedUp; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/allotment/dtos/request/DlAllotmentUpdateRequest.java b/src/main/java/com/mgm/mcrm/apigateway/allotment/dtos/request/DlAllotmentUpdateRequest.java new file mode 100644 index 0000000..603e06c --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/allotment/dtos/request/DlAllotmentUpdateRequest.java @@ -0,0 +1,14 @@ +package com.mgm.mcrm.apigateway.allotment.dtos.request; + +import lombok.Data; + +import java.time.LocalDate; + +@Data +public class DlAllotmentUpdateRequest { + private Long roomId; + private Long blockCodeId; + private Long propertyId; + private LocalDate calendarDate; + private String updateSource; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/allotment/models/CfgAppChannelDaylight.java b/src/main/java/com/mgm/mcrm/apigateway/allotment/models/CfgAppChannelDaylight.java new file mode 100644 index 0000000..a3bd3de --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/allotment/models/CfgAppChannelDaylight.java @@ -0,0 +1,30 @@ +package com.mgm.mcrm.apigateway.allotment.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name="cfg_app_channel_daylight") +@IdClass(CfgAppChannelDaylightKey.class) +public class CfgAppChannelDaylight implements Serializable { + @Id + @Column(name="criteria_id") + private Long criteriaId; + @Id + @Column(name="block_code_id") + private Long blockCodeId; + @Column(name="created_at") + private LocalDateTime createdAt; + @Column(name="created_src") + private String createdSource; + @Column(name="channel_total") + private Integer channelTotal; + @Column(name="picked_up") + private Integer pickedUp; + @Column(name="available") + private Integer available; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/allotment/models/CfgAppChannelDaylightKey.java b/src/main/java/com/mgm/mcrm/apigateway/allotment/models/CfgAppChannelDaylightKey.java new file mode 100644 index 0000000..b2d4552 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/allotment/models/CfgAppChannelDaylightKey.java @@ -0,0 +1,29 @@ +package com.mgm.mcrm.apigateway.allotment.models; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Objects; + +@Data +public class CfgAppChannelDaylightKey implements Serializable{ + + private Long criteriaId; + private Long blockCodeId; + + @Override + public boolean equals(Object o) { + if(o instanceof CfgAppChannelDaylightKey key) + { + return Objects.equals(this.criteriaId, key.getCriteriaId()) && + Objects.equals(this.blockCodeId, key.getBlockCodeId()); + } + return false ; + } + + @Override + public int hashCode() { + return 1; + } + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/allotment/models/CfgDlRoomTypeAllotment.java b/src/main/java/com/mgm/mcrm/apigateway/allotment/models/CfgDlRoomTypeAllotment.java new file mode 100644 index 0000000..ab6fd5b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/allotment/models/CfgDlRoomTypeAllotment.java @@ -0,0 +1,61 @@ +package com.mgm.mcrm.apigateway.allotment.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name="cfg_daylight_roomtype_allotment") +public class CfgDlRoomTypeAllotment { + @Id + @Column(name = "allotment_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long allotmentId; + + @Column(name="allotment_date") + private String allotmentDate; + + @Column(name="dl_room_type_id") + private String daylightRoomTypeId; + + @Column(name="dl_property_id") + private String daylightPropertyId; + + @Column(name="dl_block_code_id") + private String daylightBlockCodeId; + + @Column(name="property_id") + private Long propertyId; + + @Column(name="room_type_id") + private Long roomTypeId; + + @Column(name="block_code_id") + private Long blockCodeId; + + @Column(name="total_rooms") + private Integer totalRooms; + + @Column(name="sell_limit") + private Integer sellLimit; + + @Column(name="over_booking_limit") + private Integer overBookingLimit; + + @Column(name="all_deduct") + private Integer allDeduct; + + @Column(name="all_non_deduct") + private Integer allNonDeduct; + + @Column(name="created_at") + private LocalDateTime createdAt; + + @Column(name="updated_at") + private LocalDateTime updatedAt; + + @Column(name="updated_source") + private String updateSource; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/allotment/models/CfgRoomCriteriasDaylight.java b/src/main/java/com/mgm/mcrm/apigateway/allotment/models/CfgRoomCriteriasDaylight.java new file mode 100644 index 0000000..5022a6c --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/allotment/models/CfgRoomCriteriasDaylight.java @@ -0,0 +1,63 @@ +package com.mgm.mcrm.apigateway.allotment.models; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.mgm.mcrm.apigateway.allotment.dtos.request.BlockCodeChannelDto; +import jakarta.persistence.*; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Set; + +@Data +@Entity +@Table(name = "cfg_room_criterias_daylight") +public class CfgRoomCriteriasDaylight implements Serializable { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "property_id") + private Long propertyId; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + @Column(name = "book_date") + private LocalDateTime bookDate; + + @Column(name = "room_id") + private Long roomId; + + @Column(name = "created_at") + private LocalDateTime createdAt; + + @Column(name = "created_by") + private String createdBy; + + @Column(name = "updated_at") + private LocalDateTime updatedAt; + + @Column(name = "updated_by") + private String updatedBy; + + @Column(name = "is_waitlist") + private Integer isWaitlist; + + @Column(name = "sharer") + private Integer sharer; + + @Column(name = "dow") + private Integer bookDow; + + @Transient + private List blockCodeChannels; + + @Column(name = "rate") + private Integer rate; + + + @OneToMany(fetch = FetchType.EAGER) + @JoinColumn(name = "criteria_id", insertable = false, updatable = false) + private Set channels; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/allotment/repositories/AppChannelDaylightDao.java b/src/main/java/com/mgm/mcrm/apigateway/allotment/repositories/AppChannelDaylightDao.java new file mode 100644 index 0000000..b0dc26d --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/allotment/repositories/AppChannelDaylightDao.java @@ -0,0 +1,13 @@ +package com.mgm.mcrm.apigateway.allotment.repositories; + +import com.mgm.mcrm.apigateway.allotment.models.CfgAppChannelDaylight; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + + +public interface AppChannelDaylightDao extends JpaRepository { + CfgAppChannelDaylight findByCriteriaIdAndBlockCodeId(Long criteriaId, Long blockCodeId); + + List findAllByCriteriaIdAndBlockCodeId(Long allotmentId, Long blockCodeId); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/allotment/repositories/DaylightRoomTypeAllotmentDao.java b/src/main/java/com/mgm/mcrm/apigateway/allotment/repositories/DaylightRoomTypeAllotmentDao.java new file mode 100644 index 0000000..3d35720 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/allotment/repositories/DaylightRoomTypeAllotmentDao.java @@ -0,0 +1,30 @@ +package com.mgm.mcrm.apigateway.allotment.repositories; + +import com.mgm.mcrm.apigateway.allotment.models.CfgDlRoomTypeAllotment; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface DaylightRoomTypeAllotmentDao extends JpaRepository { + + @Query(value="select allot from CfgDlRoomTypeAllotment allot where daylightPropertyId=?1 and daylightRoomTypeId=?2 and daylightBlockCodeId=?3 and allotmentDate=?4 ") + List queryAllotmentByRoomTypeAndDate(String dlPropertyId, String dlRoomTypeId, String dlBlockCodeId, String checkDateVal); + + interface DaylightBlockCodeAllotmentSum{ + public String getCalendarDate(); + public Integer getChannelTotal(); + public Integer getPickedUp(); + public Integer getAvailable(); + } + + @Query(value = "select ?1 as calendarDate, sum(total_rooms) as channelTotal, sum(all_deduct) as pickedUp, sum(all_non_deduct) as available " + + " from cfg_daylight_roomtype_allotment " + + " where allotment_date=?1 and property_id =?2 and block_code_id =?4 and room_type_id in " + + " (select room_type_id from cfg_rooms_room_types where room_id =?3 )", nativeQuery = true) + DaylightBlockCodeAllotmentSum countRoomTypeAllotmentByBlockCode( + String calendarDate, + Long propertyId, + Long roomId, + Long blockCodeId); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/allotment/repositories/RoomCriteriaDaylightDao.java b/src/main/java/com/mgm/mcrm/apigateway/allotment/repositories/RoomCriteriaDaylightDao.java new file mode 100644 index 0000000..1fe29aa --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/allotment/repositories/RoomCriteriaDaylightDao.java @@ -0,0 +1,23 @@ +package com.mgm.mcrm.apigateway.allotment.repositories; + +import com.mgm.mcrm.apigateway.allotment.models.CfgRoomCriteriasDaylight; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.time.LocalDateTime; +import java.util.List; + + +public interface RoomCriteriaDaylightDao extends JpaRepository { + CfgRoomCriteriasDaylight findById(long id); + List findAll(Specification spec, Sort sort); + List findAll(Specification spec); + Page findAll(Specification spec, Pageable pageable); + + List findByRoomIdAndPropertyIdAndBookDate(Long roomId, Long propertyId, LocalDateTime bookDate); + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/allotment/services/RoomCriteriaDaylightService.java b/src/main/java/com/mgm/mcrm/apigateway/allotment/services/RoomCriteriaDaylightService.java new file mode 100644 index 0000000..2c75b6b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/allotment/services/RoomCriteriaDaylightService.java @@ -0,0 +1,200 @@ +package com.mgm.mcrm.apigateway.allotment.services; + + +import com.mgm.mcrm.apigateway.allotment.dtos.request.DlAllotmentUpdateRequest; +import com.mgm.mcrm.apigateway.allotment.models.CfgAppChannelDaylight; +import com.mgm.mcrm.apigateway.allotment.models.CfgDlRoomTypeAllotment; +import com.mgm.mcrm.apigateway.allotment.models.CfgRoomCriteriasDaylight; +import com.mgm.mcrm.apigateway.allotment.repositories.AppChannelDaylightDao; +import com.mgm.mcrm.apigateway.allotment.repositories.DaylightRoomTypeAllotmentDao; +import com.mgm.mcrm.apigateway.allotment.repositories.RoomCriteriaDaylightDao; +import com.mgm.mcrm.apigateway.basesetting.mapping.models.CfgBlockCodes; +import com.mgm.mcrm.apigateway.basesetting.mapping.repositories.BlockCodeDao; +import com.mgm.mcrm.apigateway.basesetting.mapping.repositories.BlockDao; +import com.mgm.mcrm.apigateway.basesetting.property.models.CfgProperties; +import com.mgm.mcrm.apigateway.basesetting.property.repositories.PropertyDao; +import com.mgm.mcrm.apigateway.basesetting.room.models.CfgRoomTypes; +import com.mgm.mcrm.apigateway.basesetting.room.repositories.RoomDao; +import com.mgm.mcrm.apigateway.basesetting.room.repositories.RoomTypeDao; +import com.mgm.mcrm.apigateway.common.utils.LogUtil; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.common.RoomCounterEntity; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; + +@Service +public class RoomCriteriaDaylightService { + + @Resource + private RoomCriteriaDaylightDao criteriaDaylightDao; + @Resource + private AppChannelDaylightDao appChannelDaylightDao; + @Resource + private PropertyDao propertyDao; + @Resource + private RoomDao roomDao; + @Resource + private BlockDao blockDao; + @Resource + private RoomTypeDao roomTypeDao; + @Resource + private BlockCodeDao blockCodeDao; + @Resource + private DaylightRoomTypeAllotmentDao daylightRoomTypeAllotmentDao; + + + public void processDaylightRoomTypeAllotmentUpdate(String updateSource, String dlPropertyId, String dlRoomTypeId, String dlBlockCodeId, String fromDate, String toDate, RoomCounterEntity roomCounter) { + if (StringUtils.isNotBlank(fromDate)) { + Long propertyId = null; + Long roomTypeId = null; + Long blockCodeId = null; + Long roomId = null; + CfgProperties cfgProperty = propertyDao.findByDlPropertyId(dlPropertyId); + if (cfgProperty != null) { + propertyId = cfgProperty.getId(); + } else { + LogUtil.warn(RoomCriteriaDaylightService.class, "[" + updateSource + "]No property config match with daylight ID[" + dlPropertyId + "]"); + } + CfgRoomTypes cfgRoomType = roomTypeDao.findByDlRoomTypeId(dlRoomTypeId); + if (propertyId!=null && cfgRoomType != null) { + roomTypeId = cfgRoomType.getId(); + roomId = roomDao.queryPropertyRoomIdByRoomTypeId(propertyId, roomTypeId); + if(roomId==null){ + LogUtil.warn(RoomCriteriaDaylightService.class, "[" + updateSource + "]No Room Category(RoomId) config match with room type ID[" + roomTypeId + "]"); + } + } else { + LogUtil.warn(RoomCriteriaDaylightService.class, "[" + updateSource + "]No Room Type config match with daylight ID[" + dlRoomTypeId + "]"); + } + if (StringUtils.isNotBlank(dlBlockCodeId)) { + CfgBlockCodes cfgBlockCode = blockCodeDao.findByDlBlockId(dlBlockCodeId); + if (cfgBlockCode != null) { + blockCodeId = cfgBlockCode.getId(); + }else{ + LogUtil.warn(RoomCriteriaDaylightService.class, "[" + updateSource + "]No Block code config match with daylight ID[" + dlBlockCodeId + "]"); + } + } + LocalDate allotmentDate = LocalDate.parse(fromDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + LocalDate endDate = null; + if (StringUtils.isNotBlank(toDate)) { + endDate = LocalDate.parse(toDate, DateTimeFormatter.ofPattern("yyyy-MM-dd")); + } else { + endDate = allotmentDate.plusDays(1); + } + List updateAllotmentRequests = new ArrayList(); + while (allotmentDate.isBefore(endDate)) { + String checkDateVal = allotmentDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); + CfgDlRoomTypeAllotment matchAllotment = null; + List roomAllotments = daylightRoomTypeAllotmentDao.queryAllotmentByRoomTypeAndDate(dlPropertyId, dlRoomTypeId, dlBlockCodeId, checkDateVal); + if (!roomAllotments.isEmpty()) { + matchAllotment = roomAllotments.get(0); + //-- Keep only one allotment for check date and room type + for (int i = 1; i < roomAllotments.size(); i++) { + daylightRoomTypeAllotmentDao.delete(roomAllotments.get(i)); + } + } + if (matchAllotment == null) { + matchAllotment = new CfgDlRoomTypeAllotment(); + matchAllotment.setAllotmentDate(checkDateVal); + matchAllotment.setDaylightPropertyId(dlPropertyId); + matchAllotment.setDaylightRoomTypeId(dlRoomTypeId); + matchAllotment.setDaylightBlockCodeId(dlBlockCodeId); + matchAllotment.setPropertyId(propertyId); + matchAllotment.setRoomTypeId(roomTypeId); + matchAllotment.setBlockCodeId(blockCodeId); + matchAllotment.setCreatedAt(LocalDateTime.now()); + } + matchAllotment.setTotalRooms(roomCounter.getTotalRooms()); + matchAllotment.setSellLimit(roomCounter.getSellLimit()); + matchAllotment.setOverBookingLimit(roomCounter.getOverbookingLimit()); + matchAllotment.setAllDeduct(roomCounter.getAllDeduct()); + matchAllotment.setAllNonDeduct(roomCounter.getAllNonDeduct()); + matchAllotment.setUpdatedAt(LocalDateTime.now()); + matchAllotment.setUpdateSource(updateSource); + daylightRoomTypeAllotmentDao.save(matchAllotment); + //--Create update request to sync daylight allotment data + if(roomId !=null && blockCodeId!=null){ + DlAllotmentUpdateRequest syncRequest = new DlAllotmentUpdateRequest(); + syncRequest.setPropertyId(propertyId); + syncRequest.setBlockCodeId(blockCodeId); + syncRequest.setRoomId(roomId); + syncRequest.setCalendarDate(allotmentDate); + syncRequest.setUpdateSource("Daylight Webhook"); + updateAllotmentRequests.add(syncRequest); + } + //-- + allotmentDate = allotmentDate.plusDays(1); + } + if(!updateAllotmentRequests.isEmpty()){ + for(DlAllotmentUpdateRequest syncRequest:updateAllotmentRequests){ + this.calculateDaylightAllotment(syncRequest); + } + } + } + } + + public void calculateDaylightAllotment(DlAllotmentUpdateRequest syncRequest) { + if(syncRequest!=null){ + CfgRoomCriteriasDaylight roomAllotment = null; + List roomCriterias = criteriaDaylightDao.findByRoomIdAndPropertyIdAndBookDate( + syncRequest.getRoomId(), + syncRequest.getPropertyId(), + syncRequest.getCalendarDate().atStartOfDay() + ); + if(!roomCriterias.isEmpty()){ + roomAllotment = roomCriterias.get(0); + if(roomCriterias.size()>1){ + //-- Remove duplicated records if has + for(int i=1;i existAppChannels = appChannelDaylightDao.findAllByCriteriaIdAndBlockCodeId(allotmentId,syncRequest.getBlockCodeId()); + if(!existAppChannels.isEmpty()){ + appChannel = existAppChannels.get(0); + for(int i=1;i { + @Query(value = "select * from cfg_settings with (nolock) where keys = ?1",nativeQuery=true) + CfgSettings getByKeys(String keys); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/appsetting/repositories/SysSettingDao.java b/src/main/java/com/mgm/mcrm/apigateway/appsetting/repositories/SysSettingDao.java new file mode 100644 index 0000000..90b3354 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/appsetting/repositories/SysSettingDao.java @@ -0,0 +1,19 @@ +package com.mgm.mcrm.apigateway.appsetting.repositories; + +import java.util.List; + +import com.mgm.mcrm.apigateway.appsetting.models.SysSetting; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + + +public interface SysSettingDao extends JpaRepository { + + @Query(value="select * from sys_settings where setting_key=?1",nativeQuery=true) + SysSetting getBySettingKey(String key); + + List findAll(Specification spec,Sort sort); + List findAll(Specification spec); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/appsetting/services/SettingService.java b/src/main/java/com/mgm/mcrm/apigateway/appsetting/services/SettingService.java new file mode 100644 index 0000000..1990d81 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/appsetting/services/SettingService.java @@ -0,0 +1,28 @@ +package com.mgm.mcrm.apigateway.appsetting.services; + +import java.util.List; + +import com.mgm.mcrm.apigateway.appsetting.models.CfgSettings; +import com.mgm.mcrm.apigateway.appsetting.repositories.SettingDao; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + + +@Service +public class SettingService { + + @Resource + private SettingDao settingDao; + + public CfgSettings getSettingByKey(String key) { + CfgSettings setting = null; + setting = settingDao.getByKeys(key); + return setting; + } + + public List getSysSettingsAll() { + return settingDao.findAll(); + } + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/appsetting/services/SysSettingService.java b/src/main/java/com/mgm/mcrm/apigateway/appsetting/services/SysSettingService.java new file mode 100644 index 0000000..f795065 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/appsetting/services/SysSettingService.java @@ -0,0 +1,32 @@ +package com.mgm.mcrm.apigateway.appsetting.services; + +import java.util.List; +import com.mgm.mcrm.apigateway.appsetting.models.SysSetting; +import com.mgm.mcrm.apigateway.appsetting.repositories.SysSettingDao; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +@Service +public class SysSettingService { + + @Resource + private SysSettingDao sysSettingDao; + + public SysSetting getSysSettingByKey(String key) { + SysSetting sysSetting = null; + sysSetting = sysSettingDao.getBySettingKey(key); + return sysSetting; + } + + public List getSysSettingsAll() { + List list = sysSettingDao.findAll(); + return list; + } + + public boolean isMockReplicate() { + SysSetting sysSetting = null; + sysSetting = sysSettingDao.getBySettingKey("APP_MOCK_REPLICATE"); + return "1".equals(sysSetting.getSettingValue()); + } + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgBlockCodes.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgBlockCodes.java new file mode 100644 index 0000000..1b4a2e1 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgBlockCodes.java @@ -0,0 +1,45 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.models; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.*; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Entity +@Table(name="cfg_block_codes") +@Data +public class CfgBlockCodes implements Serializable { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name="block_id") + private Long blockId; + @Column(name="code") + private String code; + @Column(name = "category") + private String category; + @Column(name="created_at") + private LocalDateTime createdAt; + @Column(name="created_by") + private String createdBy; + @Column(name="updated_at") + private LocalDateTime updatedAt; + @Column(name="updated_by") + private String updatedBy; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + @Column(name="start_date") + private LocalDateTime startDate; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + @Column(name="end_date") + private LocalDateTime endDate; + @Column(name="sourceName") + private String sourceName; + + @Column(name = "dl_block_id") + private String dlBlockId; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgBlocks.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgBlocks.java new file mode 100644 index 0000000..7cd492e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgBlocks.java @@ -0,0 +1,35 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Entity +@Table(name="cfg_blocks") +@Data +public class CfgBlocks implements Serializable { + //fields in table + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name="name") + private String name; + @Column(name="created_at") + private LocalDateTime createdAt; + @Column(name="created_by") + private String createdBy; + @Column(name="updated_at") + private LocalDateTime updatedAt; + @Column(name="updated_by") + private String updatedBy; + + @Transient + private Integer appChannel; + @Transient + private Integer available; + @Transient + private Integer pickedUp; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgBookingTypes.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgBookingTypes.java new file mode 100644 index 0000000..0a7a118 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgBookingTypes.java @@ -0,0 +1,29 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "cfg_booking_types") +public class CfgBookingTypes implements Serializable { + //fields in table + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "name") + private String name; + @Column(name = "created_at") + private LocalDateTime createdAt; + @Column(name = "created_by") + private String createdBy; + @Column(name = "updated_at") + private LocalDateTime updatedAt; + @Column(name = "updated_by") + private String updatedBy; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgDaylightView.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgDaylightView.java new file mode 100644 index 0000000..1bda6d4 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgDaylightView.java @@ -0,0 +1,115 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.models; + +import com.mgm.mcrm.apigateway.basesetting.property.models.CfgProperties; +import com.mgm.mcrm.apigateway.basesetting.room.models.CfgRoomTypes; +import com.mgm.mcrm.apigateway.basesetting.team.models.CfgSections; +import jakarta.persistence.*; +import lombok.Data; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Set; + +@Entity +@Table(name = "view_daylight") +@NamedEntityGraph( + name = "graph.DaylightMapping.fulldetails", + attributeNodes = { + @NamedAttributeNode("section"), + @NamedAttributeNode("property"), + @NamedAttributeNode("bookingType"), + @NamedAttributeNode("rateCode"), + @NamedAttributeNode("sourceCode"), + @NamedAttributeNode("marketCode"), + @NamedAttributeNode("daylightRoomType") + } +) +@Data +public class CfgDaylightView implements Serializable { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "section_id") + private Long sectionId; + @Column(name = "property_id") + private Long propertyId; + @Column(name = "booking_type_id") + private Long bookingTypeId; + @Column(name = "rate_code_id") + private Long rateCodeId; + @Column(name = "source_code_id") + private Long sourceCodeId; + @Column(name = "market_code_id") + private Long marketCodeId; + @Column(name = "block_code_id") + private Long blockCodeId; + @Column(name = "created_at") + private LocalDateTime createdAt; + @Column(name = "created_by") + private String createdBy; + @Column(name = "updated_at") + private LocalDateTime updatedAt; + @Column(name = "updated_by") + private String updatedBy; + @Column(name = "block_code_name") + private String blockCodeName; + @Column(name = "start_date") + private LocalDateTime startDate; + @Column(name = "end_date") + private LocalDateTime endDate; + @NotFound(action = NotFoundAction.IGNORE) + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "section_id", insertable = false, updatable = false) + private CfgSections section; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "property_id", insertable = false, updatable = false) + private CfgProperties property; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "booking_type_id", insertable = false, updatable = false) + private CfgBookingTypes bookingType; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "rate_code_id", insertable = false, updatable = false) + private CfgRateCodes rateCode; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "source_code_id", insertable = false, updatable = false) + private CfgSourceCodes sourceCode; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "market_code_id", insertable = false, updatable = false) + private CfgMarketCodes marketCode; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "cfg_daylight_mapping_room_types", joinColumns = {@JoinColumn(name = "daylight_id")}, inverseJoinColumns = {@JoinColumn(name = "room_type_id")}) + private Set daylightRoomType; + + //-- For export + @Transient + private String sectionName; + @Transient + private String propertyName; + @Transient + private String bookingTypeName; + @Transient + private String blockName; + @Transient + private String rateCodeName; + @Transient + private String roomTypeNames; + @Transient + private String sourceCodeName; + @Transient + private String marketCodeName; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgMarketCodes.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgMarketCodes.java new file mode 100644 index 0000000..500ed10 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgMarketCodes.java @@ -0,0 +1,33 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Entity +@Table(name="cfg_market_codes") +@Data +public class CfgMarketCodes implements Serializable { + //fields in table + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name="code") + private String code; + @Column(name = "category") + private String category; + @Column(name="created_at") + private LocalDateTime createdAt; + @Column(name="created_by") + private String createdBy; + @Column(name="updated_at") + private LocalDateTime updatedAt; + @Column(name="updated_by") + private String updatedBy; + + @Column(name = "dl_market_segment_id") + private String dlMarketSegmentId; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgRateCodes.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgRateCodes.java new file mode 100644 index 0000000..606dc68 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgRateCodes.java @@ -0,0 +1,32 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Entity +@Table(name = "cfg_rate_codes") +@Data +public class CfgRateCodes implements Serializable { + //fields in table + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "code") + private String code; + @Column(name = "category") + private String category; + @Column(name = "created_at") + private LocalDateTime createdAt; + @Column(name = "created_by") + private String createdBy; + @Column(name = "updated_at") + private LocalDateTime updatedAt; + @Column(name = "updated_by") + private String updatedBy; + @Column(name = "dl_rate_plan_id") + private String dlRatePlanId; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgSourceCodes.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgSourceCodes.java new file mode 100644 index 0000000..400e373 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/models/CfgSourceCodes.java @@ -0,0 +1,33 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Entity +@Table(name = "cfg_source_codes") +@Data +public class CfgSourceCodes implements Serializable { + //fields in table + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "code") + private String code; + @Column(name = "category") + private String category; + @Column(name = "created_at") + private LocalDateTime createdAt; + @Column(name = "created_by") + private String createdBy; + @Column(name = "updated_at") + private LocalDateTime updatedAt; + @Column(name = "updated_by") + private String updatedBy; + + @Column(name = "dl_source_id") + private String dlSourceId; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/BlockCodeDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/BlockCodeDao.java new file mode 100644 index 0000000..b611400 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/BlockCodeDao.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.repositories; + +import com.mgm.mcrm.apigateway.basesetting.mapping.models.CfgBlockCodes; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface BlockCodeDao extends JpaRepository { + + CfgBlockCodes findByDlBlockId(String dlBlockId); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/BlockDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/BlockDao.java new file mode 100644 index 0000000..2f6643f --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/BlockDao.java @@ -0,0 +1,15 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.repositories; + +import com.mgm.mcrm.apigateway.basesetting.mapping.models.CfgBlocks; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + + +public interface BlockDao extends JpaRepository { + CfgBlocks findById(long id); + + @Query(value="select * from cfg_blocks where name =?1",nativeQuery=true) + CfgBlocks getByName(String name); + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/DaylightViewDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/DaylightViewDao.java new file mode 100644 index 0000000..64dbc02 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/DaylightViewDao.java @@ -0,0 +1,68 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.repositories; + +import com.mgm.mcrm.apigateway.basesetting.mapping.models.CfgDaylightView; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; +import java.util.Optional; + + +public interface DaylightViewDao extends JpaRepository { + @EntityGraph(value = "graph.DaylightMapping.fulldetails", type = EntityGraph.EntityGraphType.LOAD) + CfgDaylightView findById(long id); + + @EntityGraph(value = "graph.DaylightMapping.fulldetails", type = EntityGraph.EntityGraphType.LOAD) + List findAll(Specification spec, Sort sort); + + @EntityGraph(value = "graph.DaylightMapping.fulldetails", type = EntityGraph.EntityGraphType.LOAD) + List findAll(Specification spec); + + Page findAll(Specification spec, Pageable pageable); + + @EntityGraph(value = "graph.DaylightMapping.fulldetails", type = EntityGraph.EntityGraphType.LOAD) + List findByPropertyIdAndBookingTypeId(Long propertyId, Long bookingTypeId); + + @EntityGraph(value = "graph.DaylightMapping.fulldetails",type = EntityGraph.EntityGraphType.LOAD) + List findByMarketCodeIdAndRateCodeId(Long marketCodeId, Long rateCodeId); + + @EntityGraph(value = "graph.DaylightMapping.fulldetails") + @Query(""" + SELECT d + FROM CfgDaylightView d + JOIN d.daylightRoomType rt + WHERE d.sectionId = :sectionId + AND d.propertyId = :propertyId + AND d.bookingTypeId = :bookingTypeId + AND rt.id = :roomTypeId + """) + Optional findBySectionPropertyBookingTypeRoomType( + @Param("sectionId") Long sectionId, + @Param("propertyId") Long propertyId, + @Param("bookingTypeId") Long bookingTypeId, + @Param("roomTypeId") Long roomTypeId + ); + + @EntityGraph(value = "graph.DaylightMapping.fulldetails") + @Query(""" + SELECT d + FROM CfgDaylightView d + JOIN d.daylightRoomType rt + WHERE d.sectionId = :sectionId + AND d.marketCodeId = :marketCodeId + AND d.propertyId = :propertyId + AND rt.id = :roomTypeId + """) + Optional findBySectionMarketPropertyRoomType( + @Param("sectionId") Long sectionId, + @Param("marketCodeId") Long marketCodeId, + @Param("propertyId") Long propertyId, + @Param("roomTypeId") Long roomTypeId + ); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/MarketCodeDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/MarketCodeDao.java new file mode 100644 index 0000000..bbe0a05 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/MarketCodeDao.java @@ -0,0 +1,14 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.repositories; + +import com.mgm.mcrm.apigateway.basesetting.mapping.models.CfgMarketCodes; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + + +public interface MarketCodeDao extends JpaRepository { + CfgMarketCodes findById(long id); + List findByCode(String code); + + CfgMarketCodes findByDlMarketSegmentId(String dlMarketSegmentId); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/RateCodeDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/RateCodeDao.java new file mode 100644 index 0000000..e97900c --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/RateCodeDao.java @@ -0,0 +1,13 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.repositories; + +import com.mgm.mcrm.apigateway.basesetting.mapping.models.CfgRateCodes; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface RateCodeDao extends JpaRepository { + CfgRateCodes findById(long id); + List findByCode(String code); + + CfgRateCodes findByDlRatePlanId(String dlRatePlanId); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/SourceCodeDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/SourceCodeDao.java new file mode 100644 index 0000000..7418408 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/mapping/repositories/SourceCodeDao.java @@ -0,0 +1,8 @@ +package com.mgm.mcrm.apigateway.basesetting.mapping.repositories; + +import com.mgm.mcrm.apigateway.basesetting.mapping.models.CfgSourceCodes; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface SourceCodeDao extends JpaRepository { + CfgSourceCodes findById(long id); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/property/models/CfgProperties.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/property/models/CfgProperties.java new file mode 100644 index 0000000..0fd8683 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/property/models/CfgProperties.java @@ -0,0 +1,44 @@ +package com.mgm.mcrm.apigateway.basesetting.property.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "cfg_properties") +public class CfgProperties { + + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "name") + private String name; + @Column(name = "code") + private String code; + @Column(name = "name_zht") + private String nameZht; + @Column(name = "name_zhs") + private String nameZhs; + @Column(name = "category") + private String category; + @Column(name = "hotel_code") + private String hotelCode; + @Column(name = "sort_order") + private Integer sortOrder; + @Column(name = "email_name") + private String emailName; + + @Column(name = "created_at") + private LocalDateTime createdAt; + @Column(name = "updated_at") + private LocalDateTime updatedAt; + + @Column(name = "dl_property_id") + private String dlPropertyId; + + @Column(name = "status") + private int status; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/property/repositories/PropertyDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/property/repositories/PropertyDao.java new file mode 100644 index 0000000..0e2d16b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/property/repositories/PropertyDao.java @@ -0,0 +1,24 @@ +package com.mgm.mcrm.apigateway.basesetting.property.repositories; + +import com.mgm.mcrm.apigateway.basesetting.property.models.CfgProperties; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + + +public interface PropertyDao extends JpaRepository { + + @Query(value="select * from cfg_properties where name=?1",nativeQuery=true) + CfgProperties getByName(String name); + + CfgProperties findByDlPropertyId(String dlPropertyId); + + List findByCategory(String category); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomTypes.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomTypes.java new file mode 100644 index 0000000..a3dae7b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomTypes.java @@ -0,0 +1,34 @@ +package com.mgm.mcrm.apigateway.basesetting.room.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Entity +@Table(name = "cfg_room_types") +@Data +public class CfgRoomTypes implements Serializable { + //fields in table + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "code") + private String code; + @Column(name = "name_cn") + private String nameCN; + @Column(name = "category") + private String category; + @Column(name = "created_at") + private LocalDateTime createdAt; + @Column(name = "created_by") + private String createdBy; + @Column(name = "updated_at") + private LocalDateTime updatedAt; + @Column(name = "updated_by") + private String updatedBy; + @Column(name = "dl_room_type_id") + private String dlRoomTypeId; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRooms.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRooms.java new file mode 100644 index 0000000..c56e8f7 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRooms.java @@ -0,0 +1,75 @@ +package com.mgm.mcrm.apigateway.basesetting.room.models; + +import com.mgm.mcrm.apigateway.basesetting.property.models.CfgProperties; +import jakarta.persistence.*; +import lombok.Data; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; + +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.List; + +@Entity +@Table(name = "cfg_rooms") +@NamedEntityGraph( + name = "graph.RoomSetting.fulldetails", + attributeNodes = { + @NamedAttributeNode("roomRoomType"), + @NamedAttributeNode("propertyObj") + } +) +@Data +public class CfgRooms implements Serializable { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "name") + private String name; + @Column(name = "name_cn") + private String nameCN; + @Column(name = "property_id") + private Long propertyId; + @Column(name = "sharer_limit") + private Integer sharerLimit; + @Column(name = "category") + private String category; + @Column(name = "created_at") + private LocalDateTime createdAt; + @Column(name = "created_by") + private String createdBy; + @Column(name = "updated_at") + private LocalDateTime updatedAt; + @Column(name = "updated_by") + private String updatedBy; + @Column(name = "room_category") + private String roomCategory; + @Column(name = "for_booking") + private Integer forCreateBooking; + @Column(name = "for_waitlist") + private Integer forCreateWaitlist; + @Column(name = "email_name") + private String emailName; + @Transient + private List roomTypeIds; + // For export + @Transient + private String propertyName; + @Transient + private String roomTypeNames; + @Transient + private String forCreateBookingValue; + @Transient + private String forCreateWaitlistValue; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToMany(fetch = FetchType.EAGER) + @JoinTable(name = "cfg_rooms_room_types", joinColumns = {@JoinColumn(name = "room_id")}, inverseJoinColumns = {@JoinColumn(name = "room_type_id")}) + private List roomRoomType; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "property_id", insertable = false, updatable = false) + private CfgProperties propertyObj; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomsRoomTypes.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomsRoomTypes.java new file mode 100644 index 0000000..63d3873 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomsRoomTypes.java @@ -0,0 +1,27 @@ +package com.mgm.mcrm.apigateway.basesetting.room.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name="cfg_rooms_room_types") +@IdClass(CfgRoomsRoomTypesKey.class) +public class CfgRoomsRoomTypes implements Serializable { + //fields in table + @Id + @Column(name="room_id") + private Long roomId; + @Id + @Column(name="room_type_id") + private Long roomTypeId; + @Column(name="created_at") + private LocalDateTime createdAt; + @Column(name="created_by") + private String createdBy; + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomsRoomTypesKey.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomsRoomTypesKey.java new file mode 100644 index 0000000..ceb059f --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/models/CfgRoomsRoomTypesKey.java @@ -0,0 +1,46 @@ +package com.mgm.mcrm.apigateway.basesetting.room.models; + +import java.io.Serializable; + + +public class CfgRoomsRoomTypesKey implements Serializable{ + + private Long roomId; + private Long roomTypeId; + + public Long getRoomId() { + return roomId; + } + + public void setRoomId(Long roomId) { + this.roomId = roomId; + } + + public Long getRoomTypeId() { + return roomTypeId; + } + + public void setRoomTypeId(Long roomTypeId) { + this.roomTypeId = roomTypeId; + } + + @Override + public boolean equals(Object o) { + if(o instanceof CfgRoomsRoomTypesKey) + { + CfgRoomsRoomTypesKey key = (CfgRoomsRoomTypesKey)o ; + if(this.roomId == key.getRoomId() && + this.roomTypeId == key.getRoomTypeId()) + { + return true ; + } + } + return false ; + } + + @Override + public int hashCode() { + return 1; + } + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/repositories/RoomDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/repositories/RoomDao.java new file mode 100644 index 0000000..b1394c1 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/repositories/RoomDao.java @@ -0,0 +1,27 @@ +package com.mgm.mcrm.apigateway.basesetting.room.repositories; + +import com.mgm.mcrm.apigateway.basesetting.room.models.CfgRooms; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface RoomDao extends JpaRepository { + @EntityGraph(value = "graph.RoomSetting.fulldetails",type = EntityGraph.EntityGraphType.LOAD) + CfgRooms findById(long id); + @EntityGraph(value = "graph.RoomSetting.fulldetails",type = EntityGraph.EntityGraphType.LOAD) + List findAll(Specification spec,Sort sort); + @EntityGraph(value = "graph.RoomSetting.fulldetails",type = EntityGraph.EntityGraphType.LOAD) + List findAll(Specification spec); + + Page findAll(Specification spec, Pageable pageable); + + @Query(value="select top(1) id from cfg_rooms where property_id=?1 and " + + " id in (select room_id from cfg_rooms_room_types where room_type_id =?2) ",nativeQuery=true) + Long queryPropertyRoomIdByRoomTypeId(Long propertyId, Long roomTypeId); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/repositories/RoomRoomTypeDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/repositories/RoomRoomTypeDao.java new file mode 100644 index 0000000..6b58b3b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/repositories/RoomRoomTypeDao.java @@ -0,0 +1,12 @@ +package com.mgm.mcrm.apigateway.basesetting.room.repositories; + +import com.mgm.mcrm.apigateway.basesetting.room.models.CfgRoomsRoomTypes; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface RoomRoomTypeDao extends JpaRepository { + + List findByRoomTypeId(Long roomTypeId); + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/repositories/RoomTypeDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/repositories/RoomTypeDao.java new file mode 100644 index 0000000..f7b84e4 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/room/repositories/RoomTypeDao.java @@ -0,0 +1,11 @@ +package com.mgm.mcrm.apigateway.basesetting.room.repositories; + +import com.mgm.mcrm.apigateway.basesetting.room.models.CfgRoomTypes; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface RoomTypeDao extends JpaRepository { + + CfgRoomTypes findByCodeAndCategory(String code,String category); + CfgRoomTypes findByDlRoomTypeId(String dlRoomTypeId); + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/AppUsers.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/AppUsers.java new file mode 100644 index 0000000..787c093 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/AppUsers.java @@ -0,0 +1,57 @@ +package com.mgm.mcrm.apigateway.basesetting.team.models; + +import java.io.Serializable; +import java.util.Date; + +import com.mgm.mcrm.apigateway.basesetting.property.models.CfgProperties; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +import lombok.Data; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; + +@Data +@Entity +@Table(name="app_users") +public class AppUsers implements Serializable { + //fields in table + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name="user_id") + private String userId; + @Column(name="team_id") + private Long teamId; + @Column(name="property_id") + private Long propertyId; + @Column(name="authorizer") + private String authorizer; + @Column(name="created_at") + private Date createdAt; + @Column(name="created_by") + private String createdBy; + @Column(name="updated_at") + private Date updatedAt; + @Column(name="updated_by") + private String updatedBy; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name="team_id",insertable=false ,updatable=false) + private CfgDepartments userTeam; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name="property_id",insertable=false ,updatable=false) + private CfgProperties userProperty; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgDepartments.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgDepartments.java new file mode 100644 index 0000000..07b5aff --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgDepartments.java @@ -0,0 +1,34 @@ +package com.mgm.mcrm.apigateway.basesetting.team.models; + +import java.io.Serializable; +import java.util.Date; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Data; + +@Data +@Entity +@Table(name="cfg_departments") +public class CfgDepartments { + + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name="name") + private String name; + @Column(name="created_at") + private Date createdAt; + @Column(name="updated_at") + private Date updatedAt; + @Column(name="created_by") + private String createdBy; + @Column(name="updated_by") + private String updatedBy; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgDivisions.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgDivisions.java new file mode 100644 index 0000000..b70d92e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgDivisions.java @@ -0,0 +1,34 @@ +package com.mgm.mcrm.apigateway.basesetting.team.models; + +import java.io.Serializable; +import java.util.Date; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Data; + +@Data +@Entity +@Table(name="cfg_divisions") +public class CfgDivisions { + + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name="name") + private String name; + @Column(name="created_at") + private Date createdAt; + @Column(name="created_by") + private String createdBy; + @Column(name="updated_at") + private Date updatedAt; + @Column(name="updated_by") + private String updatedBy; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgOtherUserMapping.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgOtherUserMapping.java new file mode 100644 index 0000000..6b1cf4e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgOtherUserMapping.java @@ -0,0 +1,46 @@ +package com.mgm.mcrm.apigateway.basesetting.team.models; + +import java.util.Date; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Data; + +@Data +@Entity +@Table(name="cfg_other_user_mapping") +public class CfgOtherUserMapping { + + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name="division_id") + private Long divisionId; + @Column(name="department_id") + private Long departmentId; + @Column(name="section_id") + private Long sectionId; + @Column(name="team_id") + private Long teamId; + @Column(name="property_id") + private Long propertyId; + @Column(name="booking_type_id") + private Long bookingTypeId; + @Column(name="market_code_id") + private Long marketCodeId; + @Column(name="segment_id") + private Long segmentId; + @Column(name="created_at") + private Date createdAt; + @Column(name="created_by") + private String createdBy; + @Column(name="updated_at") + private Date updatedAt; + @Column(name="updated_by") + private String updatedBy; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgSections.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgSections.java new file mode 100644 index 0000000..b5e42e3 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgSections.java @@ -0,0 +1,27 @@ +package com.mgm.mcrm.apigateway.basesetting.team.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "cfg_sections") +public class CfgSections { + + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name = "name") + private String name; + @Column(name = "created_at") + private LocalDateTime createdAt; + @Column(name = "updated_at") + private LocalDateTime updatedAt; + @Column(name = "created_by") + private String createdBy; + @Column(name = "updated_by") + private String updatedBy; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgTeamStructure.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgTeamStructure.java new file mode 100644 index 0000000..1da2e9a --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgTeamStructure.java @@ -0,0 +1,37 @@ +package com.mgm.mcrm.apigateway.basesetting.team.models; + +import java.util.Date; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.Data; + +@Data +@Entity +@Table(name="cfg_team_structure") +public class CfgTeamStructure { + + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name="division_id") + private Long divisionId; + @Column(name="department_id") + private Long departmentId; + @Column(name="section_id") + private Long sectionId; + @Column(name="created_at") + private Date createdAt; + @Column(name="updated_at") + private Date updatedAt; + @Column(name="created_by") + private String createdBy; + @Column(name="updated_by") + private String updatedBy; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgTeamStructureView.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgTeamStructureView.java new file mode 100644 index 0000000..79d7190 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/models/CfgTeamStructureView.java @@ -0,0 +1,68 @@ +package com.mgm.mcrm.apigateway.basesetting.team.models; + +import java.util.Date; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.NamedAttributeNode; +import jakarta.persistence.NamedEntityGraph; +import jakarta.persistence.NamedEntityGraphs; +import jakarta.persistence.Table; + +import lombok.Data; +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; + +@Data +@Entity +@Table(name="cfg_team_structure") +@NamedEntityGraphs({ + @NamedEntityGraph( + name = "graph.TeamStructure.fulldetails", + attributeNodes = { + @NamedAttributeNode("divisionObj"), + @NamedAttributeNode("departmentObj"), + @NamedAttributeNode("sectionObj") + } + ) +}) +public class CfgTeamStructureView { + + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name="division_id") + private Long divisionId; + @Column(name="department_id") + private Long departmentId; + @Column(name="section_id") + private Long sectionId; + @Column(name="created_at") + private Date createdAt; + @Column(name="updated_at") + private Date updatedAt; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name="division_id",insertable=false ,updatable=false) + private CfgDivisions divisionObj; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name="department_id",insertable=false ,updatable=false) + private CfgDepartments departmentObj; + + @NotFound(action = NotFoundAction.IGNORE) + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name="section_id",insertable=false ,updatable=false) + private CfgSections sectionObj; + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/AppUserDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/AppUserDao.java new file mode 100644 index 0000000..d575b3f --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/AppUserDao.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.basesetting.team.repositories; + +import com.mgm.mcrm.apigateway.basesetting.team.models.AppUsers; +import org.springframework.data.jpa.repository.JpaRepository; + + +public interface AppUserDao extends JpaRepository { + AppUsers findById(long id); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/DepartmentDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/DepartmentDao.java new file mode 100644 index 0000000..0c2c14a --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/DepartmentDao.java @@ -0,0 +1,13 @@ +package com.mgm.mcrm.apigateway.basesetting.team.repositories; + +import com.mgm.mcrm.apigateway.basesetting.team.models.CfgDepartments; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + + +public interface DepartmentDao extends JpaRepository { + + @Query(value = "select * from cfg_departments with (nolock) where name=?1", nativeQuery = true) + CfgDepartments getByName(String name); + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/DivisionDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/DivisionDao.java new file mode 100644 index 0000000..0164b76 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/DivisionDao.java @@ -0,0 +1,16 @@ +package com.mgm.mcrm.apigateway.basesetting.team.repositories; + +import com.mgm.mcrm.apigateway.basesetting.team.models.CfgDivisions; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + + +public interface DivisionDao extends JpaRepository { + CfgDivisions findById(long id); + + + @Query(value="select * from cfg_divisions where name =?1",nativeQuery=true) + CfgDivisions getByName(String name); + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/SectionDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/SectionDao.java new file mode 100644 index 0000000..059c1c5 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/SectionDao.java @@ -0,0 +1,14 @@ +package com.mgm.mcrm.apigateway.basesetting.team.repositories; + +import com.mgm.mcrm.apigateway.basesetting.team.models.CfgSections; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + + +public interface SectionDao extends JpaRepository { + + @Query(value = "select * from cfg_sections with (nolock) where name=?1", nativeQuery = true) + CfgSections getByName(String name); + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/TeamStructureDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/TeamStructureDao.java new file mode 100644 index 0000000..4ea8834 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/TeamStructureDao.java @@ -0,0 +1,14 @@ +package com.mgm.mcrm.apigateway.basesetting.team.repositories; + +import com.mgm.mcrm.apigateway.basesetting.team.models.CfgTeamStructure; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + + +public interface TeamStructureDao extends JpaRepository { + + @Query(value = "select * from cfg_team_structure with (nolock) where division_id=?1 and department_id=?2 and section_id=?3", nativeQuery = true) + CfgTeamStructure getByDivisionIdAndDepartmentIdAndSectionId(Long divisionId,Long departmentId,Long sectionId); + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/TeamStructureViewDao.java b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/TeamStructureViewDao.java new file mode 100644 index 0000000..368b46e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/basesetting/team/repositories/TeamStructureViewDao.java @@ -0,0 +1,21 @@ +package com.mgm.mcrm.apigateway.basesetting.team.repositories; + +import java.util.List; + +import com.mgm.mcrm.apigateway.basesetting.team.models.CfgTeamStructureView; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; + + +public interface TeamStructureViewDao extends JpaRepository { + + @EntityGraph(value = "graph.TeamStructure.fulldetails",type = EntityGraph.EntityGraphType.LOAD) + List findAll(Specification spec); + @EntityGraph(value = "graph.TeamStructure.fulldetails",type = EntityGraph.EntityGraphType.LOAD) + List findAll(Specification spec, Sort sort); + + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/DataSourceConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/DataSourceConfig.java new file mode 100644 index 0000000..d202c0e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/DataSourceConfig.java @@ -0,0 +1,30 @@ +package com.mgm.mcrm.apigateway.common.config; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.jdbc.DataSourceBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +import javax.sql.DataSource; + + +@Configuration +public class DataSourceConfig { + + @Bean(name = "mgmDataSource") + @Qualifier("mgmDataSource") + @ConfigurationProperties(prefix = "spring.datasource.mgm") + @Primary + public DataSource mgmDataSource() { + return DataSourceBuilder.create().build(); + } + + @Bean(name = "operaDataSource") + @Qualifier("operaDataSource") + @ConfigurationProperties(prefix = "spring.datasource.opera") + public DataSource operaDataSource() { + return DataSourceBuilder.create().build(); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/DaylightProxyConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/DaylightProxyConfig.java new file mode 100644 index 0000000..a6b792d --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/DaylightProxyConfig.java @@ -0,0 +1,89 @@ +package com.mgm.mcrm.apigateway.common.config; + +import com.mgm.mcrm.apigateway.common.constants.SysCommon; +import com.mgm.mcrm.apigateway.common.dtos.config.DaylightApiSetting; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class DaylightProxyConfig { + + @Value("${mcrm.proxy.api-config.daylight.server-url}") + private String serverUrl; + + @Value("${mcrm.proxy.api-config.daylight.request-timeout:60}") + private int requestTimeout; + + @Value("${mcrm.proxy.api-config.daylight.token.token-url}") + private String tokenUrl; + + @Value("${mcrm.proxy.api-config.daylight.token.username}") + private String userName; + + @Value("${mcrm.proxy.api-config.daylight.token.password}") + private String password; + + @Value("${mcrm.proxy.api-config.daylight.token.grant-type}") + private String grantType; + + @Value("${mcrm.proxy.api-config.daylight.token.client-id}") + private String clientId; + + @Value("${mcrm.proxy.api-config.daylight.token.client-secret}") + private String clientSecret; + + @Value("${mcrm.proxy.api-config.daylight.token.request-timeout:60}") + private int tokenRequestTimeout; + + @Value("${mcrm.proxy.api-config.daylight.token.default-expiry-second:800}") + private long tokenExpirySecond; + + @Value("${mcrm.proxy.api-config.daylight.security.authorization-data}") + private String appAuthorizationData; + + //-- Proxy setting for daylight + @Value("${mcrm.proxy.api-config.daylight.proxy-setting.host}") + private String proxyHost; + @Value("${mcrm.proxy.api-config.daylight.proxy-setting.port:443}") + private String proxyPort; + @Value("${mcrm.proxy.api-config.daylight.proxy-setting.auth.username}") + private String proxyUsername; + @Value("${mcrm.proxy.api-config.daylight.proxy-setting.auth.password}") + private String proxyPassword; + //-- SSL config for daylight + @Value("${mcrm.proxy.api-config.daylight.ssl-bundle-name}") + private String sslBundleName; + + @Bean + public DaylightApiSetting configDaylightApi() { + DaylightApiSetting config = new DaylightApiSetting(); + config.setServerUrl(serverUrl); + config.setApiTimeout(requestTimeout); + config.setTokenUrl(tokenUrl); + config.setUsername(userName); + config.setPassword(password); + config.setGrantType(grantType); + config.setClientId(clientId); + config.setClientSecret(clientSecret); + config.setTokenRequestTimeout(tokenRequestTimeout); + config.setTokenExpireSecond(tokenExpirySecond); + //-- + config.setProxyHost(proxyHost); + config.setProxyUserName(proxyUsername); + config.setProxyPassword(proxyPassword); + int proxyPortValue = 443; + try{ + proxyPortValue = Integer.parseInt(proxyPort); + }catch(Exception ignored){ + + } + config.setProxyPort(proxyPortValue); + config.setSslBundleName(sslBundleName); + //-- + config.registerAuthorizationList(SysCommon.extractAuthorizationItems(appAuthorizationData)); + return config; + } + +} + diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/EmailProxyConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/EmailProxyConfig.java new file mode 100644 index 0000000..b400c5a --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/EmailProxyConfig.java @@ -0,0 +1,52 @@ +package com.mgm.mcrm.apigateway.common.config; + +import com.mgm.mcrm.apigateway.common.constants.SysCommon; +import com.mgm.mcrm.apigateway.common.dtos.config.DaylightApiSetting; +import com.mgm.mcrm.apigateway.common.dtos.config.EmailProxySetting; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class EmailProxyConfig { + + @Value("${mcrm.proxy.api-config.notification.email-sender.smtp-server}") + private String smtpServer; + + @Value("${mcrm.proxy.api-config.notification.email-sender.smtp-port}") + private int smtpPort; + + @Value("${mcrm.proxy.api-config.notification.email-sender.username}") + private String userName; + + @Value("${mcrm.proxy.api-config.notification.email-sender.password}") + private String password; + + @Value("${mcrm.proxy.api-config.notification.email-sender.smtp-auth}") + private boolean smtpAuth; + + @Value("${mcrm.proxy.api-config.notification.email-sender.starttls}") + private boolean startTls; + + @Value("${mcrm.proxy.api-config.notification.email-sender.default-send-to}") + private String defaultSentTo; + + @Value("${mcrm.proxy.api-config.notification.security.authorization-data}") + private String appAuthorizationData; + + @Bean + public EmailProxySetting configEmailProxy() { + EmailProxySetting config = new EmailProxySetting(); + config.setSmtpServer(smtpServer); + config.setSmtpPort(smtpPort); + config.setUsername(userName); + config.setPassword(password); + config.setAuth(smtpAuth); + config.setStarttls(startTls); + config.setDefaultSendTo(defaultSentTo); + config.registerAuthorizationList(SysCommon.extractAuthorizationItems(appAuthorizationData)); + return config; + } + +} + diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/EsbProxyConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/EsbProxyConfig.java new file mode 100644 index 0000000..26de60d --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/EsbProxyConfig.java @@ -0,0 +1,77 @@ +package com.mgm.mcrm.apigateway.common.config; + +import com.mgm.mcrm.apigateway.common.constants.SysCommon; +import com.mgm.mcrm.apigateway.common.dtos.config.EsbApiSetting; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class EsbProxyConfig { + + @Value("${mcrm.proxy.api-config.esb.server-url}") + private String serverUrl; + + @Value("${mcrm.proxy.api-config.esb.request-timeout:60}") + private int apiRequestTimeout; + + @Value("${mcrm.proxy.api-config.esb.token.token-url}") + private String tokenUrl; + + @Value("${mcrm.proxy.api-config.esb.token.app-id}") + private String appId; + + @Value("${mcrm.proxy.api-config.esb.token.app-secret}") + private String appSecret; + + @Value("${mcrm.proxy.api-config.esb.token.request-timeout:60}") + private int tokenRequestTimeout; + + @Value("${mcrm.proxy.api-config.esb.token.default-expiry-second:800}") + private long tokenExpirySecond; + + @Value("${mcrm.proxy.api-config.esb.security.authorization-data}") + private String appAuthorizationData; + + //-- Proxy setting for esb + @Value("${mcrm.proxy.api-config.esb.proxy-setting.host}") + private String proxyHost; + @Value("${mcrm.proxy.api-config.esb.proxy-setting.port:443}") + private String proxyPort; + @Value("${mcrm.proxy.api-config.esb.proxy-setting.auth.username}") + private String proxyUsername; + @Value("${mcrm.proxy.api-config.esb.proxy-setting.auth.password}") + private String proxyPassword; + //-- SSL config for esb + @Value("${mcrm.proxy.api-config.esb.ssl-bundle-name}") + private String sslBundleName; + + @Bean + public EsbApiSetting configEsbApi() { + EsbApiSetting config = new EsbApiSetting(); + config.setServerUrl(serverUrl); + config.setApiRequestTimeout(apiRequestTimeout); + config.setTokenUrl(tokenUrl); + config.setAppId(appId); + config.setAppSecret(appSecret); + config.setTokenRequestTimeout(tokenRequestTimeout); + config.setTokenExpireSecond(tokenExpirySecond); + //-- + config.setProxyHost(proxyHost); + config.setProxyUserName(proxyUsername); + config.setProxyPassword(proxyPassword); + int proxyPortValue = 443; + try{ + proxyPortValue = Integer.parseInt(proxyPort); + }catch(Exception ignored){ + + } + config.setProxyPort(proxyPortValue); + config.setSslBundleName(sslBundleName); + //-- + config.registerAuthorizationList(SysCommon.extractAuthorizationItems(appAuthorizationData)); + return config; + } +} + diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/HttpHeader.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/HttpHeader.java new file mode 100644 index 0000000..0d38765 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/HttpHeader.java @@ -0,0 +1,26 @@ +package com.mgm.mcrm.apigateway.common.config; + +import lombok.Data; + +/** + * HTTP 請求頭配置類 + * 用於自定義 HTTP 請求頭的值 + * + * @author System + */ +@Data +public class HttpHeader { + public static final String ACCEPT = "Accept"; + public static final String ACCEPT_CHARSET = "Accept-Charset"; + public static final String ACCEPT_ENCODING = "Accept-Encoding"; + public static final String ACCEPT_LANGUAGE = "Accept-Language"; + public static final String ACCEPT_RANGES = "Accept-Ranges"; + public static final String AUTHORIZATION = "Authorization"; + public static final String CACHE_CONTROL = "Cache-Control"; + public static final String CONNECTION = "Connection"; + public static final String COOKIE = "Cookie"; + public static final String CONTENT_LENGTH = "Content-Length"; + public static final String CONTENT_TYPE = "Content-Type"; + public static final String USER_AGENT = "user-agent"; +} + diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/MGMDataSourceConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/MGMDataSourceConfig.java new file mode 100644 index 0000000..d3e28d6 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/MGMDataSourceConfig.java @@ -0,0 +1,72 @@ +package com.mgm.mcrm.apigateway.common.config; + +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.sql.DataSource; + +/** + * @Description: 主数据源配置 + * @date + */ +@Configuration +@EnableTransactionManagement +@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", + transactionManagerRef = "transactionManager", + basePackages = {"com.mgm.mcrm.apigateway"}) +public class MGMDataSourceConfig { + + @Autowired + @Qualifier("mgmDataSource") + private DataSource mrbsDataSource; + + @Primary + @Bean(name = "mgmJpaProperties") + @ConfigurationProperties(prefix = "spring.jpa.mgm") + public JpaProperties jpaProperties() { + return new JpaProperties(); + } + + @Primary + @Bean(name = "entityManager") + public EntityManager entityManager( @Qualifier("entityManagerFactory") EntityManagerFactory factory) { + return factory.createEntityManager(); + } + + @Primary + @Bean(name = "entityManagerFactory") //mrbs实体工厂 + public LocalContainerEntityManagerFactoryBean entityManagerFactory (@Qualifier("mgmDataSource") DataSource mgmDataSource, + @Qualifier("mgmJpaProperties") JpaProperties jpaProperties,EntityManagerFactoryBuilder builder) { + return builder.dataSource(mgmDataSource) + .properties(jpaProperties.getProperties()) + .properties( + new HibernateProperties() + .determineHibernateProperties( + jpaProperties.getProperties(), new HibernateSettings())) + .packages("com.mgm.mcrm.apigateway") //实体类所在位置 + .persistenceUnit("mgmPersistenceUnit") + .build(); + } + + @Primary + @Bean(name = "transactionManager") + public PlatformTransactionManager transactionManager(@Qualifier("entityManagerFactory") EntityManagerFactory factory) { + return new JpaTransactionManager(factory); + } + +} \ No newline at end of file diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/OperaDataSourceConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/OperaDataSourceConfig.java new file mode 100644 index 0000000..c55c83e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/OperaDataSourceConfig.java @@ -0,0 +1,66 @@ +package com.mgm.mcrm.apigateway.common.config; + +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.sql.DataSource; + +/** + * @Description: 副数据源配置 + * @date + */ +@Configuration +@EnableTransactionManagement +@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactoryOpera", + transactionManagerRef = "transactionManagerOpera") +public class OperaDataSourceConfig { + + @Autowired + @Qualifier("operaDataSource") + private DataSource operaDataSource; + + @Bean(name = "operaJpaProperties") + @ConfigurationProperties(prefix = "spring.jpa.opera") + public JpaProperties jpaProperties() { + return new JpaProperties(); + } + + @Bean(name = "entityManagerOpera") + public EntityManager entityManager( @Qualifier("entityManagerFactoryOpera") EntityManagerFactory factory) { + return factory.createEntityManager(); + } + + @Bean(name = "entityManagerFactoryOpera") + public LocalContainerEntityManagerFactoryBean entityManagerFactoryOpera (@Qualifier("operaDataSource") DataSource operaDataSource, + @Qualifier("operaJpaProperties") JpaProperties jpaProperties,EntityManagerFactoryBuilder builder) { + return builder.dataSource(operaDataSource) + .properties(jpaProperties.getProperties()) + .properties( + new HibernateProperties() + .determineHibernateProperties( + jpaProperties.getProperties(), new HibernateSettings())) + .packages("com.mgm.daylight.booking.models") //实体类所在位置 + .persistenceUnit("operaPersistenceUnit") + .build(); + } + + @Bean(name = "transactionManagerOpera") + public PlatformTransactionManager transactionManagerOpera(@Qualifier("entityManagerFactoryOpera") EntityManagerFactory factory) { + return new JpaTransactionManager(factory); + } + +} \ No newline at end of file diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/OperaProxyConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/OperaProxyConfig.java new file mode 100644 index 0000000..0756935 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/OperaProxyConfig.java @@ -0,0 +1,57 @@ +package com.mgm.mcrm.apigateway.common.config; + +import com.mgm.mcrm.apigateway.common.constants.SysCommon; +import com.mgm.mcrm.apigateway.common.dtos.config.OperaApiSetting; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class OperaProxyConfig { + + @Value("${mcrm.proxy.api-config.opera.server-url}") + private String serverUrl; + + @Value("${mcrm.proxy.api-config.opera.request-timeout:10000}") + private int requestTimeout; + + @Value("${mcrm.proxy.api-config.opera.security.authorization-data}") + private String appAuthorizationData; + + //-- Proxy setting for opera + @Value("${mcrm.proxy.api-config.opera.proxy-setting.host}") + private String proxyHost; + @Value("${mcrm.proxy.api-config.opera.proxy-setting.port:443}") + private String proxyPort; + @Value("${mcrm.proxy.api-config.opera.proxy-setting.auth.username}") + private String proxyUsername; + @Value("${mcrm.proxy.api-config.opera.proxy-setting.auth.password}") + private String proxyPassword; + //-- SSL config for opera + @Value("${mcrm.proxy.api-config.opera.ssl-bundle-name}") + private String sslBundleName; + + @Bean + public OperaApiSetting configOperaApi() { + OperaApiSetting config = new OperaApiSetting(); + config.setServerUrl(serverUrl); + config.setTimeout(requestTimeout); + //-- + config.setProxyHost(proxyHost); + config.setProxyUserName(proxyUsername); + config.setProxyPassword(proxyPassword); + int proxyPortValue = 443; + try{ + proxyPortValue = Integer.parseInt(proxyPort); + }catch(Exception ignored){ + + } + config.setProxyPort(proxyPortValue); + config.setSslBundleName(sslBundleName); + //-- + config.registerAuthorizationList(SysCommon.extractAuthorizationItems(appAuthorizationData)); + return config; + } + +} + diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/SwaggerConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/SwaggerConfig.java new file mode 100644 index 0000000..cabeaca --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/SwaggerConfig.java @@ -0,0 +1,18 @@ +package com.mgm.mcrm.apigateway.common.config; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + @Bean + public OpenAPI customOpenAPI() { + return new OpenAPI() + .info(new Info() + .title("API Document for MGM MCRM API Gateway Application") + .version("1.0") + .description("API Document for MGM MCRM API Gateway Application")); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/config/WebFilterConfig.java b/src/main/java/com/mgm/mcrm/apigateway/common/config/WebFilterConfig.java new file mode 100644 index 0000000..7018b01 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/config/WebFilterConfig.java @@ -0,0 +1,32 @@ +package com.mgm.mcrm.apigateway.common.config; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; +import com.mgm.mcrm.apigateway.logging.filters.WebhookLoggingFilter; + +@Configuration +public class WebFilterConfig { + + @Bean + public TaskScheduler taskScheduler() { + ThreadPoolTaskScheduler scheduling = new ThreadPoolTaskScheduler(); + scheduling.setPoolSize(20); + scheduling.initialize(); + return scheduling; + } + + @Bean + public FilterRegistrationBean registerWebhookFilter( + WebhookLoggingFilter webhookLoggingFilter) { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(webhookLoggingFilter); + registration.addUrlPatterns("/api/daylight/webhook/*"); + registration.setOrder(3); + registration.setName("webhookLoggingFilter"); + return registration; + } + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/constants/Constants.java b/src/main/java/com/mgm/mcrm/apigateway/common/constants/Constants.java new file mode 100644 index 0000000..2ca3c89 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/constants/Constants.java @@ -0,0 +1,173 @@ +package com.mgm.mcrm.apigateway.common.constants; + + +import java.util.Locale; + +/** + * 通用常量信息 + * + * @author Aaka + */ +public class Constants +{ + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * 系统语言 + */ + public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + public static final String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + public static final String FAIL = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 所有权限标识 + */ + public static final String ALL_PERMISSION = "*:*:*"; + + /** + * 管理员角色权限标识 + */ + public static final String SUPER_ADMIN = "admin"; + + /** + * 角色权限分隔符 + */ + public static final String ROLE_DELIMETER = ","; + + /** + * 权限标识分隔符 + */ + public static final String PERMISSION_DELIMETER = ","; + + /** + * 验证码有效期(分钟) + */ + public static final Integer CAPTCHA_EXPIRATION = 2; + + /** + * 令牌 + */ + public static final String TOKEN = "token"; + + /** + * 令牌前缀 + */ + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * 令牌前缀 + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + + /** + * 用户ID + */ + public static final String JWT_USERID = "userid"; + + /** + * 用户名称 + */ +// public static final String JWT_USERNAME = Claims.SUBJECT; + + /** + * 用户头像 + */ + public static final String JWT_AVATAR = "avatar"; + + /** + * 创建时间 + */ + public static final String JWT_CREATED = "created"; + + /** + * 用户权限 + */ + public static final String JWT_AUTHORITIES = "authorities"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + + /** + * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) + */ + public static final String[] JSON_WHITELIST_STR = { "com.ruoyi" }; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" }; + + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config", "com.ruoyi.generator" }; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/constants/SettingConstants.java b/src/main/java/com/mgm/mcrm/apigateway/common/constants/SettingConstants.java new file mode 100644 index 0000000..fe0ac6d --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/constants/SettingConstants.java @@ -0,0 +1,60 @@ +package com.mgm.mcrm.apigateway.common.constants; + +public class SettingConstants { + public static final String ENABLE_CUSTOM_DATE = "ENABLE_CUSTOM_DATE"; + public static final String CUSTOM_CUR_DATE = "CUSTOM_CUR_DATE"; + public static final String ENABLE_EXT_IP_CHECK = "ENABLE_EXT_IP_CHECK"; + public static final String ENABLE_EXTERNAL_SEARCH_RESTRICTION = "ENABLE_EXTERNAL_SEARCH_RESTRICTION"; + public static final String ENABLE_DEFAULT_ROOM_PRICE = "ENABLE_DEFAULT_ROOM_PRICE"; + public static final String ENABLE_ROOM_EXTENSION = "ENABLE_ROOM_EXTENSION"; + + public static final String BOOKING_MAX_NIGHTS = "BOOKING_MAX_NIGHTS"; + public static final String BOOKING_MAX_ROOMS = "BOOKING_MAX_ROOMS"; + public static final String BOOKING_MAX_FUTURE_DATE = "BOOKING_MAX_FUTURE_DATE"; + public static final String BOOKING_MAX_OCCUPANCY_PER_ROOM = "BOOKING_MAX_OCCUPANCY_PER_ROOM"; + + public static final String CANCEL_CUTOFF_TIME = "CANCEL_CUTOFF_TIME"; + + public static final String ENABLE_AUTO_DEPLOY = "ENABLE_AUTO_DEPLOY"; + public static final String ENABLE_OWS_TRACE = "ENABLE_OWS_TRACE"; + public static final String ENABLE_UUID_CHECK = "ENABLE_UUID_CHECK"; + public static final String ENABLE_ADMINER = "ENABLE_ADMINER"; + public static final String SESSION_MAX_IDLE_MINUTES = "SESSION_MAX_IDLE_MINUTES"; + public static final String GCM_COMMENT_TYPE = "GCM_COMMENT_TYPE"; + public static final String ENABLE_TRACE_LOG = "ENABLE_TRACE_LOG"; + + //phase 2 + public static final String ENABLE_APPROVAL = "ENABLE_APPROVAL"; + public static final String APPROVAL_EXPIRE_MINUTES = "APPROVAL_EXPIRE_MINUTES"; + + //ows fix + public static final String OWS_FETCH_MAX_BOOKINGS = "OWS_FETCH_MAX_BOOKINGS"; + + public static final String[] EXTENDABLE_STATUSES = {"CHECKED_IN","DUE_OUT"}; + + //ApprovalConstants + public static final String APPROVAL_PERIOD = "APPROVAL_PERIOD"; + public static final String GCOMP_BALANCE = "GCOMP_BALANCE"; + public static final String DCOMP_BALANCE = "DCOMP_BALANCE"; + public static final String ROOM_EXTENSION = "ROOM_EXTENSION"; + public static final String ADD_SHARE = "ADD_SHARE"; + + //ApprovalPeriod + public static final Integer APPROVAL_PERIOD_TYPE_NEW_BOOKING = 1; + public static final Integer APPROVAL_PERIOD_TYPE_EXTENSION = 2; + + public static final Integer MEMBER_ID_LENGTH = 8; + + //RoomRCDRate + public static final Integer BOOKING_TYPE_NEW = 1; + public static final Integer BOOKING_TYPE_EXTENSION = 2; + + //NotificationHelper + public static final Integer EMAIL_TYPE_NEW_BOOKING = 1; + public static final Integer EMAIL_TYPE_MODIFY = 2; + public static final Integer EMAIL_TYPE_EXTEND = 3; + public static final Integer EMAIL_TYPE_CANCEL = 4; + +} + + diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/constants/SysCommon.java b/src/main/java/com/mgm/mcrm/apigateway/common/constants/SysCommon.java new file mode 100644 index 0000000..1600bfb --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/constants/SysCommon.java @@ -0,0 +1,102 @@ +package com.mgm.mcrm.apigateway.common.constants; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.*; + + +import com.mgm.mcrm.apigateway.common.utils.LogUtil; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.system.ApplicationHome; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + + +public class SysCommon { + + public static String runtime_path = ""; + public static String runtimeName="crm_gateway_runtime"; + public static boolean isMock = true; + + public static String getSystemPath() { + ApplicationHome h = new ApplicationHome(SysCommon.class); + File f = h.getSource(); + return f.getParentFile().toString(); + } + + public static void initRuntimePath() { + runtime_path = getSystemPath(); + } + + + //-- Path Type 1= Class path 2=Application path 3=URI Path + public static String loadResFileContentByPath(String filePath, int pathType) { + String fileContent = ""; + InputStream ifs = null; + BufferedReader bufferedReader = null; + InputStreamReader fileReader = null; + try { + if(pathType==2) { + String applicationPath = SysCommon.runtime_path+"/"+runtimeName; + File dataFile = new File(applicationPath+filePath); + ifs=new FileInputStream(dataFile); + }else { + Resource resource = new ClassPathResource(filePath); + ifs = resource.getInputStream(); + } + fileReader = new InputStreamReader(ifs, "UTF-8"); + bufferedReader = new BufferedReader(fileReader); + String lineData = null; + int lineCnt =0; + StringBuffer contentBuffer = new StringBuffer(); + while ((lineData = bufferedReader.readLine()) != null) { + if(lineCnt>0) { + contentBuffer.append("\n"); + } + contentBuffer.append(lineData); + lineCnt++; + } + bufferedReader.close(); + fileContent = contentBuffer.toString(); + } catch (Exception e) { + //e.printStackTrace(); + LogUtil.error(SysCommon.class, "Get Data File failed:"+e.getMessage()); + } finally { + try { + if (bufferedReader != null) { + bufferedReader.close(); + } + if(fileReader!=null) { + fileReader.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + //-- + return fileContent; + } + + public static Map extractAuthorizationItems(String appAuthorizationData){ + Map result = new HashMap<>(); + if(StringUtils.isNotBlank(appAuthorizationData)){ + String[] securityItems = appAuthorizationData.split("[;,]"); + for(String securityItem:securityItems){ + String[] itemTags = securityItem.split("/"); + if(itemTags.length>=2){ + String appId = itemTags[0].replace("[","").replace("]","").trim().toUpperCase(); + String securityKey = itemTags[1].replace("[","").replace("]","").trim(); + if(StringUtils.isNotBlank(appId) && StringUtils.isNotBlank(securityKey)){ + result.put(appId,securityKey); + } + } + } + } + return result; + } + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/constants/SysConstants.java b/src/main/java/com/mgm/mcrm/apigateway/common/constants/SysConstants.java new file mode 100644 index 0000000..b9120a4 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/constants/SysConstants.java @@ -0,0 +1,48 @@ +package com.mgm.mcrm.apigateway.common.constants; + +public class SysConstants { + + public static final String CURRENT_USER_KEY = "currentUser"; + public static final String SUPER_ADMIN_ID ="1"; + public static final int ACTIVE = 1; + public static final int INACTIVE = 0; + + public static final int STATUS_OK =200; + public static final int STATUS_AUTH_X_FAILED =401; + public static final int STATUS_PARAMETER_ERROR =422; + public static final int STATUS_GAME_NOT_AVAILABLE =301; + public static final int STATUS_NO_GAME_CHANCE =302; + public static final int STATUS_NO_RESULT =303; + public static final int STATUS_DUPLICATED =304; + public static final int STATUS_REQUEST_INVALID =305; + public static final int STATUS_REQUEST_TIMEOUT =306; + public static final int STATUS_INVALID_REWARD_SETTING =307; + public static final int STATUS_GAME_SESSION_TIMEOUT =308; + public static final int STATUS_NO_CUST_TIER =309; + public static final int STATUS_NO_REWARD =310; + public static final int STATUS_REQUEST_ERROR =701; + + public static final String TIER_RED="Red"; + public static final String TIER_GOLD="Gold"; + public static final String TIER_DIAMOND="Diamond"; + public static final String TIER_CHAIRMAN="Chairman"; + + // cfg_settings + public static final String MAX_BOOKING_STAY_DAYS="MAX_BOOKING_STAY_DAYS"; + public static final String MAX_ADVANCE_BOOKING_DAYS="MAX_ADVANCE_BOOKING_DAYS"; + public static final String NUM_OF_MULTIPLE_DATE_RANGE="NUM_OF_MULTIPLE_DATE_RANGE"; + public static final String PROTECTION_PERIOD_CHECKIN="PROTECTION_PERIOD_CHECKIN"; + public static final String PROTECTION_PERIOD_CHECKOUT="PROTECTION_PERIOD_CHECKOUT"; + public static final String PROTECT_PERIOD_ENABLE="PROTECT_PERIOD_ENABLE"; + public static final String MAX_PREVIOUS_BOOKING_DAYS="MAX_PREVIOUS_BOOKING_DAYS"; + + // Token + public static final int CMS_TOKEN_TYPE=0; + public static final int MOBILE_TOKEN_TYPE=1; + + public static final String PERMISSION_APP_VIEW_BOOKING="10001_01"; + public static final String PERMISSION_APP_NEW_BOOKING="10001_02"; + public static final String PERMISSION_APP_TODO_LIST="10001_03"; + public static final String PERMISSION_APP_DASHBOARD="10001_04"; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/BaseApiResponse.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/BaseApiResponse.java new file mode 100644 index 0000000..0106876 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/BaseApiResponse.java @@ -0,0 +1,12 @@ +package com.mgm.mcrm.apigateway.common.dtos; + +import lombok.Data; + +@Data +public class BaseApiResponse { + private boolean success = false; + private String code; + private String message; + private String content; + private int statusCode=-1; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/CachedToken.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/CachedToken.java new file mode 100644 index 0000000..82081a8 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/CachedToken.java @@ -0,0 +1,21 @@ +package com.mgm.mcrm.apigateway.common.dtos; + +import com.mgm.mcrm.apigateway.daylight.common.dtos.TokenResponse; +import lombok.Getter; + +public class CachedToken { + + @Getter + private final TokenResponse token; + private final long expireTime; + + public CachedToken(TokenResponse token, long expireTime) { + this.token = token; + this.expireTime = expireTime; + } + + + public boolean isExpired() { + return System.currentTimeMillis() > expireTime; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/HttpReqResp.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/HttpReqResp.java new file mode 100644 index 0000000..2ea868b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/HttpReqResp.java @@ -0,0 +1,69 @@ +package com.mgm.mcrm.apigateway.common.dtos; + + +import lombok.Data; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpResponse; +import org.apache.hc.core5.http.ProtocolVersion; +import org.apache.hc.core5.http.message.StatusLine; + +@Data +public class HttpReqResp { + + /** + * 执行结果-body + */ + private String result; + + /** + * 状态码-statusCode + */ + private int statusCode; + + /** + * 状态行-StatusLine + */ + private StatusLine statusLine; + + /** + * 请求头信息 + */ + private Header[] reqHeaders; + + /** + * 响应头信息 + */ + private Header[] respHeaders; + + /** + * 协议版本 + */ + private ProtocolVersion protocolVersion; + + /** + * HttpResponse结果对象 + */ + private HttpResponse resp; + + public HttpReqResp(HttpResponse resp) { + // HttpClient 5 API: 使用 new StatusLine(response) 创建 StatusLine + this.statusLine = new StatusLine(resp); + // HttpClient 5 API: 使用 getHeaders() 获取所有响应头 + this.respHeaders = resp.getHeaders(); + // HttpClient 5 API: 从 StatusLine 获取协议版本 + this.protocolVersion = this.statusLine.getProtocolVersion(); + // HttpClient 5 API: 使用 getCode() 获取状态码 + this.statusCode = resp.getCode(); + this.resp = resp; + } + + /** + * 从返回的头信息中查询指定头信息 + * + * @param name 头信息名称 + */ + public Header getHeaders(final String name) { + Header[] headers = this.resp.getHeaders(name); + return headers!=null && headers.length>0?headers[0]:null; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/HttpResult.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/HttpResult.java new file mode 100644 index 0000000..d15e087 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/HttpResult.java @@ -0,0 +1,15 @@ +package com.mgm.mcrm.apigateway.common.dtos; + +import lombok.Data; +import org.springframework.http.HttpHeaders; + +@Data +public class HttpResult { + private String result; + private int statusCode; + private HttpHeaders responseHeaders; + private String responseBody; + private String errorMessage; + private String exceptionClass; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/ResFileResult.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/ResFileResult.java new file mode 100644 index 0000000..020d9d9 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/ResFileResult.java @@ -0,0 +1,11 @@ +package com.mgm.mcrm.apigateway.common.dtos; + +import lombok.Data; + +@Data +public class ResFileResult { + private boolean success = false; + private String fileName; + private String filePath; + private String errorMessage; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/ServiceResult.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/ServiceResult.java new file mode 100644 index 0000000..05a4799 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/ServiceResult.java @@ -0,0 +1,12 @@ +package com.mgm.mcrm.apigateway.common.dtos; + +import lombok.Data; + +@Data +public class ServiceResult { + private String errorCode; + private boolean isSuccess=false; + private String errorMessage=""; + private Object returnData =null; + private Object oldData=null; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/DaylightApiSetting.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/DaylightApiSetting.java new file mode 100644 index 0000000..1b0fa5f --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/DaylightApiSetting.java @@ -0,0 +1,47 @@ +package com.mgm.mcrm.apigateway.common.dtos.config; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import org.springframework.beans.factory.annotation.Value; + +import java.util.HashMap; +import java.util.Map; + +@Data +public class DaylightApiSetting { + + //-- Daylight Server + private String serverUrl=""; + private int apiTimeout=0; + private String tokenUrl=""; + private String username; + private String password; + private String grantType; + private String clientId; + private String clientSecret; + //--Proxy config + private String proxyHost; + private int proxyPort; + private String proxyUserName; + private String proxyPassword; + //--SSL config + private String sslBundleName; + + private int tokenRequestTimeout; + private long tokenExpireSecond; + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private final Map authorizationConfig = new HashMap<>(); + + public void registerAuthorizationList(Map authItems){ + if(authItems!=null){ + authorizationConfig.putAll(authItems); + } + } + + public String getAppAuthorizatinKey(String appId){ + return authorizationConfig.getOrDefault(appId, null); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/EmailProxySetting.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/EmailProxySetting.java new file mode 100644 index 0000000..29331e1 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/EmailProxySetting.java @@ -0,0 +1,34 @@ +package com.mgm.mcrm.apigateway.common.dtos.config; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.util.HashMap; +import java.util.Map; + +@Data +public class EmailProxySetting { + + private int smtpPort=25; + private String smtpServer= ""; + private String username= ""; + private String password= ""; + private boolean auth=false; + private boolean starttls=false; + private String defaultSendTo=""; + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private final Map authorizationConfig = new HashMap<>(); + + public void registerAuthorizationList(Map authItems){ + if(authItems!=null){ + authorizationConfig.putAll(authItems); + } + } + + public String getAppAuthorizatinKey(String appId){ + return authorizationConfig.getOrDefault(appId, null); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/EsbApiSetting.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/EsbApiSetting.java new file mode 100644 index 0000000..dc89ed0 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/EsbApiSetting.java @@ -0,0 +1,42 @@ +package com.mgm.mcrm.apigateway.common.dtos.config; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.util.HashMap; +import java.util.Map; + +@Data +public class EsbApiSetting { + //-- ESB Server + private String serverUrl=""; + private int apiRequestTimeout; + private String tokenUrl=""; + private String appId=""; + private String appSecret=""; + //--Proxy config + private String proxyHost; + private int proxyPort; + private String proxyUserName; + private String proxyPassword; + //--SSL config + private String sslBundleName; + + private int tokenRequestTimeout; + private long tokenExpireSecond; + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private final Map authorizationConfig = new HashMap<>(); + + public void registerAuthorizationList(Map authItems){ + if(authItems!=null){ + authorizationConfig.putAll(authItems); + } + } + + public String getAppAuthorizatinKey(String appId){ + return authorizationConfig.getOrDefault(appId, null); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/OperaApiSetting.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/OperaApiSetting.java new file mode 100644 index 0000000..1573da9 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/config/OperaApiSetting.java @@ -0,0 +1,38 @@ +package com.mgm.mcrm.apigateway.common.dtos.config; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; + +import java.util.HashMap; +import java.util.Map; + +@Data +public class OperaApiSetting { + //-- OWS Server + private String serverUrl= ""; + private int timeout=0; + + //--Proxy config + private String proxyHost; + private int proxyPort; + private String proxyUserName; + private String proxyPassword; + //--SSL config + private String sslBundleName; + + @Getter(AccessLevel.NONE) + @Setter(AccessLevel.NONE) + private final Map authorizationConfig = new HashMap<>(); + + public void registerAuthorizationList(Map authItems){ + if(authItems!=null){ + authorizationConfig.putAll(authItems); + } + } + + public String getAppAuthorizatinKey(String appId){ + return authorizationConfig.getOrDefault(appId, null); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/webhook/BatchWebhookEventEntry.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/webhook/BatchWebhookEventEntry.java new file mode 100644 index 0000000..d07a59d --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/webhook/BatchWebhookEventEntry.java @@ -0,0 +1,17 @@ +package com.mgm.mcrm.apigateway.common.dtos.webhook; + +import lombok.Data; + +import java.time.Instant; + +@Data +public class BatchWebhookEventEntry { + private Instant timestamp; + private String messageId; + private String propertyId; + private String topic; + private String eventType; + private String correlationId; + private String payload; + private long batchSequence; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/webhook/ShijiBatchWebhookEvent.java b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/webhook/ShijiBatchWebhookEvent.java new file mode 100644 index 0000000..37c50c9 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/webhook/ShijiBatchWebhookEvent.java @@ -0,0 +1,11 @@ +package com.mgm.mcrm.apigateway.common.dtos.webhook; + +import lombok.Data; + +import java.util.List; + +@Data +public class ShijiBatchWebhookEvent { + private List results; + private int count; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/enums/DeliveryReceiptState.java b/src/main/java/com/mgm/mcrm/apigateway/common/enums/DeliveryReceiptState.java new file mode 100644 index 0000000..70df84c --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/enums/DeliveryReceiptState.java @@ -0,0 +1,56 @@ +package com.mgm.mcrm.apigateway.common.enums; + +public enum DeliveryReceiptState { + + ESME_ROK(0, "Ok - Message Acceptable"), + + ESME_RINVMSGLEN(1, "Invalid Message Length"), + + ESME_RINVCMDLEN(2, "Invalid Command Length"), + + ESME_RINVCMDID(3, "Invalid Command ID"), + + ESME_RINVBNDSTS(4, "Invalid bind status"), + + ESME_RALYBND(5, "Bind attempted when already bound"), + + ESME_RINVPRTFLG(6, "Invalid priority flag"), + + ESME_RINVREGDLVFLG(7, "Invalid registered-delivery flag"), + + ESME_RSYSERR(8, "SMSC system error"), + + ESME_RINVSRCADR(9, "Invalid source address"), + + ESME_RINVDSTADR(11, "Invalid destination address"), + + ESME_RINVMSGID(12, "Invalid message-id"), + + NOT_FOUND(000, "Couldn't resolve.Ask admin to add."); + + private int value; + private String description; + + DeliveryReceiptState(int value, String description) { + this.value = value; + this.description = description; + } + + public static DeliveryReceiptState getDescription(int value) { + for (DeliveryReceiptState item : values()) { + if (item.value() == value) { + return item; + } + } + return NOT_FOUND; + } + + public int value() { + return value; + } + + public String description() { + return description; + } + +} \ No newline at end of file diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/enums/EumnDaylightStatus.java b/src/main/java/com/mgm/mcrm/apigateway/common/enums/EumnDaylightStatus.java new file mode 100644 index 0000000..781bd06 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/enums/EumnDaylightStatus.java @@ -0,0 +1,87 @@ +package com.mgm.mcrm.apigateway.common.enums; + +public enum EumnDaylightStatus { + + DUE_IN("DUE IN","DUE_IN"), + DUE_IN_UNDERSCORE("DUE_IN","DUE_IN"), + DUEIN("DUEIN","DUE_IN"), + RESERVED("RESERVED","RESERVED"), + RESERVE("Reserved","RESERVED"), + CHECKEDIN("Checked in","CHECKED_IN"), + CHECKED_IN("CHECKED IN","CHECKED_IN"), + CHECKED_IN_UNDERSCORE("CHECKED_IN","CHECKED_IN"), + CHECK_IN("CHECK_IN","CHECKED_IN"), + CHECK_IN_IN_HOUSE("CHECK_IN(IN_HOUSE)","CHECKED_IN"), + CHECKED_OUT("CHECKED OUT","CHECKED_OUT"), + CHECKED_OUT_UNDERSCORE("CHECKED_OUT","CHECKED_OUT"), + INHOUSE("INHOUSE","CHECKED_IN"), + IN_HOUSE("IN-HOUSE","CHECKED_IN"), + IN_HOUSE_UNDERSCORE("IN_HOUSE","CHECKED_IN"), + IN("IN","CHECKED_IN"), + IH("IH","CHECKED_IN"), + DUE_OUT("DUE OUT","DUE_OUT"), + DUE_OUT_UNDERSCORE("DUE_OUT","DUE_OUT"), + DUEOUT("DUEOUT","DUE_OUT"), + CANCELED("CANCELED","CANCELED"), + CANCELLED("CANCELLED","CANCELED"), + NO_SHOW("NO SHOW","CANCELED"), + NOSHOW("NOSHOW","CANCELED"); + + private String key; + private String value; + + EumnDaylightStatus(String key, String value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + public static String getStatusByDaylightStatus(String key) { + if (key == null) { + return "UNKNOWN"; + } + String normalizedKey = key.trim(); + EumnDaylightStatus[] objs = values(); + for (EumnDaylightStatus obj : objs) { + if (obj.getKey().equalsIgnoreCase(normalizedKey)) { + return obj.getValue(); + } + } + String compactKey = normalizeStatusKey(normalizedKey); + for (EumnDaylightStatus obj : objs) { + String compactObjKey = normalizeStatusKey(obj.getKey()); + if (compactObjKey.equalsIgnoreCase(compactKey)) { + return obj.getValue(); + } + } + if (compactKey.contains("CHECKIN") || compactKey.contains("INHOUSE")) { + return "CHECKED_IN"; + } + if (compactKey.contains("CANCEL")) { + return "CANCELED"; + } + return "UNKNOWN"; + } + + private static String normalizeStatusKey(String key) { + return key == null ? "" : key.replaceAll("[^A-Za-z0-9]", ""); + } + + public static boolean isInclude(String value){ + boolean include = false; + for (EumnDaylightStatus e: EumnDaylightStatus.values()){ + if(e.getValue().equals(value)){ + include = true; + break; + } + } + return include; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/enums/HttpMethods.java b/src/main/java/com/mgm/mcrm/apigateway/common/enums/HttpMethods.java new file mode 100644 index 0000000..5a472a7 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/enums/HttpMethods.java @@ -0,0 +1,74 @@ +package com.mgm.mcrm.apigateway.common.enums; + + +/** + * 枚举HttpMethods方法 + * + * @author Anka + */ +public enum HttpMethods { + + /** + * 求获取Request-URI所标识的资源 + */ + GET(0, "GET"), + + /** + * 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。 + * POST请求可能会导致新的资源的建立和/或已有资源的修改 + */ + POST(1, "POST"), + + /** + * 向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。 + * 这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息 + * 只获取响应信息报头 + */ + HEAD(2, "HEAD"), + + /** + * 向指定资源位置上传其最新内容(全部更新,操作幂等) + */ + PUT (3, "PUT"), + + /** + * 请求服务器删除Request-URI所标识的资源 + */ + DELETE (4, "DELETE"), + + /** + * 请求服务器回送收到的请求信息,主要用于测试或诊断 + */ + TRACE(5, "TRACE"), + + /** + * 向指定资源位置上传其最新内容(部分更新,非幂等) + */ + PATCH (6, "PATCH"), + + /** + * 返回服务器针对特定资源所支持的HTTP请求方法。 + * 也可以利用向Web服务器发送'*'的请求来测试服务器的功能性 + */ + OPTIONS (7, "OPTIONS"), + +// /** +// * HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器 +// */ +// CONNECT(99, "CONNECT"), + ; + + private int code; + private String name; + + private HttpMethods(int code, String name){ + this.code = code; + this.name = name; + } + public String getName() { + return name; + } + public int getCode() { + return code; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/exception/NoAccessException.java b/src/main/java/com/mgm/mcrm/apigateway/common/exception/NoAccessException.java new file mode 100644 index 0000000..625e907 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/exception/NoAccessException.java @@ -0,0 +1,8 @@ +package com.mgm.mcrm.apigateway.common.exception; + +public class NoAccessException extends RuntimeException { + + public NoAccessException(String message) { + super(message); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpCookieContext.java b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpCookieContext.java new file mode 100644 index 0000000..fecc325 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpCookieContext.java @@ -0,0 +1,46 @@ +package com.mgm.mcrm.apigateway.common.httpclient; + +import org.apache.hc.client5.http.cookie.BasicCookieStore; +import org.apache.hc.client5.http.cookie.CookieStore; +import org.apache.hc.client5.http.protocol.HttpClientContext; + +public class HttpCookieContext { + /** + * 使用httpcontext,用于设置和携带Cookie + */ + private HttpClientContext context ; + + /** + * 储存Cookie + */ + private CookieStore cookieStore; + + public static HttpCookieContext custom(){ + return new HttpCookieContext(); + } + + private HttpCookieContext(){ + this.context = new HttpClientContext(); + this.cookieStore = new BasicCookieStore(); + this.context.setCookieStore(cookieStore); + } + + public HttpClientContext getContext() { + return context; + } + + public HttpCookieContext setContext(HttpClientContext context) { + this.context = context; + return this; + } + + public CookieStore getCookieStore() { + return cookieStore; + } + + public HttpCookieContext setCookieStore(CookieStore cookieStore) { + this.cookieStore = cookieStore; + return this; + } + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpEntityBuilder.java b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpEntityBuilder.java new file mode 100644 index 0000000..7d77162 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpEntityBuilder.java @@ -0,0 +1,310 @@ +package com.mgm.mcrm.apigateway.common.httpclient; + +import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; +import org.apache.hc.client5.http.entity.mime.HttpMultipartMode; +import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; +import org.apache.hc.core5.http.ContentType; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.HttpHeaders; +import org.apache.hc.core5.http.NameValuePair; +import org.apache.hc.core5.http.io.entity.ByteArrayEntity; +import org.apache.hc.core5.http.io.entity.FileEntity; +import org.apache.hc.core5.http.io.entity.StringEntity; +import org.apache.hc.core5.http.message.BasicHeader; +import org.apache.hc.core5.http.message.BasicNameValuePair; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.nio.charset.Charset; +import java.util.*; +import java.util.Map.Entry; + + +public class HttpEntityBuilder { + + //传入参数特定类型 + public static final String ENTITY_STRING="$ENTITY_STRING$"; + public static final String ENTITY_JSON="$ENTITY_JSON$"; + public static final String ENTITY_FILE="$ENTITY_FILEE$"; + public static final String ENTITY_BYTES="$ENTITY_BYTES$"; + public static final String ENTITY_INPUTSTREAM="$ENTITY_INPUTSTREAM$"; + public static final String ENTITY_SERIALIZABLE="$ENTITY_SERIALIZABLE$"; + public static final String ENTITY_MULTIPART="$ENTITY_MULTIPART$"; + private static final List SPECIAL_ENTITIY = Arrays.asList(ENTITY_STRING, ENTITY_JSON, ENTITY_BYTES, ENTITY_FILE, ENTITY_INPUTSTREAM, ENTITY_SERIALIZABLE, ENTITY_MULTIPART); + + /** + * 是否开启debug, + */ + private static boolean debug = false; + + private static final Logger log = LoggerFactory.getLogger(HttpEntityBuilder.class); + + + /** + * 检测url是否含有参数,如果有,则把参数加到参数列表中 + * + * @param url 资源地址 + * @param nvps 参数列表 + * @param encoding 编码 + * @return 返回去掉参数的url + * @throws UnsupportedEncodingException 不支持的编码异常 + */ + public static String checkHasParas(String url, List nvps, String encoding) throws UnsupportedEncodingException { + // 检测url中是否存在参数 + if (url.contains("?") && url.indexOf("?") < url.indexOf("=")) { + String[] paras = url.substring(url.indexOf("?") + 1).split("&"); + for (String para : paras) { + int pos = para.indexOf("="); + if (pos >= 0) { + nvps.add(new BasicNameValuePair(para.substring(0, pos), para.substring(pos + 1))); + } else { + nvps.add(new BasicNameValuePair(para, "")); + } + } + url = url.substring(0, url.indexOf("?")); + } + return url; + } + + /** + * + * 参数转换,将map中的参数,转到参数列表中 + * + * @param nvps 参数列表 + * @param map 参数列表(map) + * @param encoding 编码 + * @return 返回HttpEntity + * @throws UnsupportedEncodingException 不支持的编码异常 + */ + public static HttpEntity map2HttpEntity(List nvps, Map map, String encoding) throws UnsupportedEncodingException { + HttpEntity entity = null; + if(map!=null && map.size()>0){ + boolean isSpecial = false; + // 拼接参数 + for (Entry entry : map.entrySet()) { + if(SPECIAL_ENTITIY.contains(entry.getKey())){//判断是否在之中 + isSpecial = true; + if(ENTITY_STRING.equals(entry.getKey())){//string + entity = new StringEntity(String.valueOf(entry.getValue()), Charset.forName(encoding)); + break; + }else if(ENTITY_JSON.equals(entry.getKey())){//json + ContentType jsonContentType; + + entity = new StringEntity(String.valueOf(entry.getValue()), Charset.forName(encoding)); + String contentType = "application/json"; + if (encoding != null && !encoding.trim().isEmpty()) { + // 创建带有编码的JSON ContentType + jsonContentType = ContentType.APPLICATION_JSON.withCharset(Charset.forName(encoding)); + }else { + // 使用默认的JSON ContentType(通常UTF-8) + jsonContentType = ContentType.APPLICATION_JSON; + } + // 创建实体时直接传入ContentType + entity = new StringEntity(String.valueOf(entry.getValue()), jsonContentType); + break; + }else if(ENTITY_BYTES.equals(entry.getKey())){//file + byte[] data = (byte[]) entry.getValue(); + entity = new ByteArrayEntity(data, ContentType.APPLICATION_OCTET_STREAM); + break; + }else if(ENTITY_FILE.equals(entry.getKey())){//file + if(File.class.isAssignableFrom(entry.getValue().getClass())){ + entity = new FileEntity((File)entry.getValue(), ContentType.APPLICATION_OCTET_STREAM); + }else if(entry.getValue().getClass()==String.class){ + entity = new FileEntity(new File((String) entry.getValue()), ContentType.create("text/plain", "UTF-8")); + } + break; + }else if(ENTITY_INPUTSTREAM.equals(entry.getKey())){//inputstream +// entity = new InputStreamEntity(); + break; + }else if(ENTITY_SERIALIZABLE.equals(entry.getKey())){//serializeable +// entity = new SerializableEntity() + break; + }else if(ENTITY_MULTIPART.equals(entry.getKey())){//MultipartEntityBuilder + File[] files = null; + if(File.class.isAssignableFrom(entry.getValue().getClass().getComponentType())){ + files=(File[])entry.getValue(); + }else if(entry.getValue().getClass().getComponentType()==String.class){ + String[] names = (String[]) entry.getValue(); + files = new File[names.length]; + for (int i = 0; i < names.length; i++) { + files[i] = new File(names[i]); + } + } + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + builder.setCharset(Charset.forName(encoding));// 设置请求的编码格式 + builder.setMode(HttpMultipartMode.LEGACY);// 设置浏览器兼容模式 + int count = 0; + for (File file : files) { +// //把文件转换成流对象FileBody +// FileBody fileBody = new FileBody(file); +// builder.addPart(String.valueOf(map.get(ENTITY_MULTIPART+".name")) + count++, fileBody); + builder.addBinaryBody(String.valueOf(map.get(ENTITY_MULTIPART+".name")) + count++,file); + } + boolean forceRemoveContentTypeCharset = (boolean)map.get(ENTITY_MULTIPART+".rmCharset"); + Map m = new HashMap(); + m.putAll(map); + m.remove(ENTITY_MULTIPART); + m.remove(ENTITY_MULTIPART+".name"); + m.remove(ENTITY_MULTIPART+".rmCharset"); + Iterator> iterator = m.entrySet().iterator(); + // 发送的数据 + while (iterator.hasNext()) { + Entry e = iterator.next(); + builder.addTextBody(e.getKey(), String.valueOf(e.getValue()), ContentType.create("text/plain", encoding)); + } + entity = builder.build();// 生成 HTTP POST 实体 + + //强制去除contentType中的编码设置,否则,在某些情况下会导致上传失败 + if(forceRemoveContentTypeCharset){ + removeContentTypeCharset(encoding, entity); + } + break; + }else { + nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()))); + } + }else{ + nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue()))); + } + } + if(!isSpecial) { + entity = new UrlEncodedFormEntity(nvps, Charset.forName(encoding)); + } + } + return entity; + } + + /** + * 移除content-type中的charset + * + * @param encoding 编码 + * @param entity 请求参数及数据信息 + */ + private static void removeContentTypeCharset(String encoding, HttpEntity entity) { + try { + Class clazz = entity.getClass(); + Field field = clazz.getDeclaredField("contentType"); + field.setAccessible(true); //将字段的访问权限设为true:即去除private修饰符的影响 + if(Modifier.isFinal(field.getModifiers())){ + Field modifiersField = Field.class.getDeclaredField("modifiers"); //去除final修饰符的影响,将字段设为可修改的 + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + } + BasicHeader o = (BasicHeader) field.get(entity); + field.set(entity, new BasicHeader(HttpHeaders.CONTENT_TYPE, o.getValue().replace("; charset="+encoding,""))); + } catch (NoSuchFieldException e) { + HttpEntityBuilder.exception(e); + } catch (SecurityException e) { + HttpEntityBuilder.exception(e); + } catch (IllegalArgumentException e) { + HttpEntityBuilder.exception(e); + } catch (IllegalAccessException e) { + HttpEntityBuilder.exception(e); + } + } + + + /** + * 生成参数 + * 参数格式:k1=v1&k2=v2 + * + * @param paras 参数列表 + * @return 返回参数列表(map) + */ + public static Map buildParas(String paras){ + String[] p = paras.split("&"); + String[][] ps = new String[p.length][2]; + int pos = 0; + for (int i = 0; i < p.length; i++) { + pos = p[i].indexOf("="); + ps[i][0]=p[i].substring(0,pos); + ps[i][1]=p[i].substring(pos+1); + pos = 0; + } + return buildParas(ps); + } + + /** + * 生成参数 + * 参数类型:{{"k1","v1"},{"k2","v2"}} + * + * @param paras 参数列表 + * @return 返回参数列表(map) + */ + public static Map buildParas(String[][] paras){ + // 创建参数队列 + Map map = new HashMap(); + for (String[] para: paras) { + map.put(para[0], para[1]); + } + return map; + } + + /** + * 打印消息 + * + * @param msg 消息 + */ + public static void info(String msg){ + if(debug){ + log.info(msg); + } + } + + /** + * 打印消息和异常堆栈 + * + * @param msg 异常消息 + * @param t 异常 + */ + public static void infoException(String msg, Throwable t){ + if(debug){ + log.info(msg,t); + } + } + + /** + * 打印错误消息 + * + * @param msg 异常消息 + */ + public static void error(String msg){ + log.error(msg); + } + + /** + * 打印错误消息和异常堆栈 + * + * @param msg 异常消息 + * @param t 异常 + */ + public static void errorException(String msg, Throwable t){ + log.error(msg, t); + } + + /** + * 打印异常堆栈 + * + * @param t 异常 + */ + public static void exception(Throwable t){ + log.error(String.valueOf(t)); + } + + /** + * 开启打印日志 + */ + public static void debug() { + debug(true); + } + /** + * 开启或关闭打印日志 + * @param debug 是否开启debug + */ + public static void debug(boolean debug) { + HttpEntityBuilder.debug = debug; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpHeaderBuilder.java b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpHeaderBuilder.java new file mode 100644 index 0000000..d01c747 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpHeaderBuilder.java @@ -0,0 +1,697 @@ +package com.mgm.mcrm.apigateway.common.httpclient; + + +import com.mgm.mcrm.apigateway.common.config.HttpHeader; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.message.BasicHeader; + +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + + +/** + * http 发送参数 + * + * @author Aaka + */ +public class HttpHeaderBuilder { + + private HttpHeaderBuilder() {}; + + public static HttpHeaderBuilder custom() { + return new HttpHeaderBuilder(); + } + + //记录head头信息 + private Map headerMaps = new HashMap(); + + /** + * 自定义header头信息 + * + * @param key header-key + * @param value header-value + * @return 返回当前对象 + */ + public HttpHeaderBuilder other(String key, String value) { + headerMaps.put(key, new BasicHeader(key, value)); + return this; + } + /** + * 指定客户端能够接收的内容类型 + * 例如:Accept: text/plain, text/html + * + * @param accept accept + * @return 返回当前对象 + */ + public HttpHeaderBuilder accept(String accept) { + headerMaps.put(HttpReqHead.ACCEPT, + new BasicHeader(HttpReqHead.ACCEPT, accept)); + return this; + } + + /** + * 浏览器可以接受的字符编码集 + * 例如:Accept-Charset: iso-8859-5 + * + * @param acceptCharset accept-charset + * @return 返回当前对象 + */ + public HttpHeaderBuilder acceptCharset(String acceptCharset) { + headerMaps.put(HttpReqHead.ACCEPT_CHARSET, + new BasicHeader(HttpReqHead.ACCEPT_CHARSET, acceptCharset)); + return this; + } + + /** + * 指定浏览器可以支持的web服务器返回内容压缩编码类型 + * 例如:Accept-Encoding: compress, gzip + * + * @param acceptEncoding accept-encoding + * @return 返回当前对象 + */ + public HttpHeaderBuilder acceptEncoding(String acceptEncoding) { + headerMaps.put(HttpReqHead.ACCEPT_ENCODING, + new BasicHeader(HttpReqHead.ACCEPT_ENCODING, acceptEncoding)); + return this; + } + + /** + * 浏览器可接受的语言 + * 例如:Accept-Language: en,zh + * + * @param acceptLanguage accept-language + * @return 返回当前对象 + */ + public HttpHeaderBuilder acceptLanguage(String acceptLanguage) { + headerMaps.put(HttpReqHead.ACCEPT_LANGUAGE, + new BasicHeader(HttpReqHead.ACCEPT_LANGUAGE, acceptLanguage)); + return this; + } + + /** + * 可以请求网页实体的一个或者多个子范围字段 + * 例如:Accept-Ranges: bytes + * + * @param acceptRanges accept-ranges + * @return 返回当前对象 + */ + public HttpHeaderBuilder acceptRanges(String acceptRanges) { + headerMaps.put(HttpReqHead.ACCEPT_RANGES, + new BasicHeader(HttpReqHead.ACCEPT_RANGES, acceptRanges)); + return this; + } + + /** + * HTTP授权的授权证书 + * 例如:Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== + * + * @param authorization authorization + * @return 返回当前对象 + */ + public HttpHeaderBuilder authorization(String authorization) { + headerMaps.put(HttpReqHead.AUTHORIZATION, + new BasicHeader(HttpReqHead.AUTHORIZATION, authorization)); + return this; + } + + public HttpHeaderBuilder ac_property_id(String ac_property_id) { + headerMaps.put(HttpReqHead.AC_PROPERTY_ID, + new BasicHeader(HttpReqHead.AC_PROPERTY_ID, ac_property_id)); + return this; + } + + /** + * 指定请求和响应遵循的缓存机制 + * 例如:Cache-Control: no-cache + * + * @param cacheControl cache-control + * @return 返回当前对象 + */ + public HttpHeaderBuilder cacheControl(String cacheControl) { + headerMaps.put(HttpReqHead.CACHE_CONTROL, + new BasicHeader(HttpReqHead.CACHE_CONTROL, cacheControl)); + return this; + } + + /** + * 表示是否需要持久连接(HTTP 1.1默认进行持久连接) + * 例如:Connection: close 短链接; Connection: keep-alive 长连接 + * + * @param connection connection + * @return 返回当前对象 + */ + public HttpHeaderBuilder connection(String connection) { + headerMaps.put(HttpReqHead.CONNECTION, + new BasicHeader(HttpReqHead.CONNECTION, connection)); + return this; + } + + /** + * HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器 + * 例如:Cookie: $Version=1; Skin=new; + * + * @param cookie cookie + * @return 返回当前对象 + */ + public HttpHeaderBuilder cookie(String cookie) { + headerMaps.put(HttpReqHead.COOKIE, + new BasicHeader(HttpReqHead.COOKIE, cookie)); + return this; + } + + /** + * 请求内容长度 + * 例如:Content-Length: 348 + * + * @param contentLength content-length + * @return 返回当前对象 + */ + public HttpHeaderBuilder contentLength(String contentLength) { + headerMaps.put(HttpReqHead.CONTENT_LENGTH, + new BasicHeader(HttpReqHead.CONTENT_LENGTH, contentLength)); + return this; + } + + /** + * 请求的与实体对应的MIME信息 + * 例如:Content-Type: application/x-www-form-urlencoded + * + * @param contentType content-type + * @return 返回当前对象 + */ + public HttpHeaderBuilder contentType(String contentType) { + headerMaps.put(HttpReqHead.CONTENT_TYPE, + new BasicHeader(HttpReqHead.CONTENT_TYPE, contentType)); + return this; + } + + /** + * 请求发送的日期和时间 + * 例如:Date: Tue, 15 Nov 2010 08:12:31 GMT + * + * @param date date + * @return 返回当前对象 + */ + public HttpHeaderBuilder date(String date) { + headerMaps.put(HttpReqHead.DATE, + new BasicHeader(HttpReqHead.DATE, date)); + return this; + } + + /** + * 请求的特定的服务器行为 + * 例如:Expect: 100-continue + * + * @param expect expect + * @return 返回当前对象 + */ + public HttpHeaderBuilder expect(String expect) { + headerMaps.put(HttpReqHead.EXPECT, + new BasicHeader(HttpReqHead.EXPECT, expect)); + return this; + } + + /** + * 发出请求的用户的Email + * 例如:From: user@email.com + * + * @param from from + * @return 返回当前对象 + */ + public HttpHeaderBuilder from(String from) { + headerMaps.put(HttpReqHead.FROM, + new BasicHeader(HttpReqHead.FROM, from)); + return this; + } + + /** + * 指定请求的服务器的域名和端口号 + * 例如:Host: blog.csdn.net + * + * @param host host + * @return 返回当前对象 + */ + public HttpHeaderBuilder host(String host) { + headerMaps.put(HttpReqHead.HOST, + new BasicHeader(HttpReqHead.HOST, host)); + return this; + } + + /** + * 只有请求内容与实体相匹配才有效 + * 例如:If-Match: “737060cd8c284d8af7ad3082f209582d” + * + * @param ifMatch if-match + * @return 返回当前对象 + */ + public HttpHeaderBuilder ifMatch(String ifMatch) { + headerMaps.put(HttpReqHead.IF_MATCH, + new BasicHeader(HttpReqHead.IF_MATCH, ifMatch)); + return this; + } + + /** + * 如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码 + * 例如:If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT + * + * @param ifModifiedSince if-modified-Since + * @return 返回当前对象 + */ + public HttpHeaderBuilder ifModifiedSince(String ifModifiedSince) { + headerMaps.put(HttpReqHead.IF_MODIFIED_SINCE, + new BasicHeader(HttpReqHead.IF_MODIFIED_SINCE, ifModifiedSince)); + return this; + } + + /** + * 如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变 + * 例如:If-None-Match: “737060cd8c284d8af7ad3082f209582d” + * + * @param ifNoneMatch if-none-match + * @return 返回当前对象 + */ + public HttpHeaderBuilder ifNoneMatch(String ifNoneMatch) { + headerMaps.put(HttpReqHead.IF_NONE_MATCH, + new BasicHeader(HttpReqHead.IF_NONE_MATCH, ifNoneMatch)); + return this; + } + + /** + * 如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag + * 例如:If-Range: “737060cd8c284d8af7ad3082f209582d” + * + * @param ifRange if-range + * @return 返回当前对象 + */ + public HttpHeaderBuilder ifRange(String ifRange) { + headerMaps.put(HttpReqHead.IF_RANGE, + new BasicHeader(HttpReqHead.IF_RANGE, ifRange)); + return this; + } + + /** + * 只在实体在指定时间之后未被修改才请求成功 + * 例如:If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT + * + * @param ifUnmodifiedSince if-unmodified-since + * @return 返回当前对象 + */ + public HttpHeaderBuilder ifUnmodifiedSince(String ifUnmodifiedSince) { + headerMaps.put(HttpReqHead.IF_UNMODIFIED_SINCE, + new BasicHeader(HttpReqHead.IF_UNMODIFIED_SINCE, ifUnmodifiedSince)); + return this; + } + + /** + * 限制信息通过代理和网关传送的时间 + * 例如:Max-Forwards: 10 + * + * @param maxForwards max-forwards + * @return 返回当前对象 + */ + public HttpHeaderBuilder maxForwards(String maxForwards) { + headerMaps.put(HttpReqHead.MAX_FORWARDS, + new BasicHeader(HttpReqHead.MAX_FORWARDS, maxForwards)); + return this; + } + + /** + * 用来包含实现特定的指令 + * 例如:Pragma: no-cache + * + * @param pragma pragma + * @return 返回当前对象 + */ + public HttpHeaderBuilder pragma(String pragma) { + headerMaps.put(HttpReqHead.PRAGMA, + new BasicHeader(HttpReqHead.PRAGMA, pragma)); + return this; + } + + /** + * 连接到代理的授权证书 + * 例如:Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== + * + * @param proxyAuthorization proxy-authorization + * @return 返回当前对象 + */ + public HttpHeaderBuilder proxyAuthorization(String proxyAuthorization) { + headerMaps.put(HttpReqHead.PROXY_AUTHORIZATION, + new BasicHeader(HttpReqHead.PROXY_AUTHORIZATION, proxyAuthorization)); + return this; + } + + /** + * 只请求实体的一部分,指定范围 + * 例如:Range: bytes=500-999 + * + * @param range range + * @return 返回当前对象 + */ + public HttpHeaderBuilder range(String range) { + headerMaps.put(HttpReqHead.RANGE, + new BasicHeader(HttpReqHead.RANGE, range)); + return this; + } + + /** + * 先前网页的地址,当前请求网页紧随其后,即来路 + * 例如:Referer: http://www.zcmhi.com/archives/71.html + * + * @param referer referer + * @return 返回当前对象 + */ + public HttpHeaderBuilder referer(String referer) { + headerMaps.put(HttpReqHead.REFERER, + new BasicHeader(HttpReqHead.REFERER, referer)); + return this; + } + + /** + * 客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息 + * 例如:TE: trailers,deflate;q=0.5 + * + * @param te te + * @return 返回当前对象 + */ + public HttpHeaderBuilder te(String te) { + headerMaps.put(HttpReqHead.TE, + new BasicHeader(HttpReqHead.TE, te)); + return this; + } + + /** + * 向服务器指定某种传输协议以便服务器进行转换(如果支持) + * 例如:Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11 + * + * @param upgrade upgrade + * @return 返回当前对象 + */ + public HttpHeaderBuilder upgrade(String upgrade) { + headerMaps.put(HttpReqHead.UPGRADE, + new BasicHeader(HttpReqHead.UPGRADE, upgrade)); + return this; + } + + /** + * User-Agent的内容包含发出请求的用户信息 + * + * @param userAgent user-agent + * @return 返回当前对象 + */ + public HttpHeaderBuilder userAgent(String userAgent) { + headerMaps.put(HttpReqHead.USER_AGENT, + new BasicHeader(HttpReqHead.USER_AGENT, userAgent)); + return this; + } + + /** + * 关于消息实体的警告信息 + * 例如:Warn: 199 Miscellaneous warning + * + * @param warning warning + * @return 返回当前对象 + */ + public HttpHeaderBuilder warning(String warning) { + headerMaps.put(HttpReqHead.WARNING, + new BasicHeader(HttpReqHead.WARNING, warning)); + return this; + } + + /** + * 通知中间网关或代理服务器地址,通信协议 + * 例如:Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1) + * + * @param via via + * @return 返回当前对象 + */ + public HttpHeaderBuilder via(String via) { + headerMaps.put(HttpReqHead.VIA, + new BasicHeader(HttpReqHead.VIA, via)); + return this; + } + + /** + * 设置此HTTP连接的持续时间(超时时间) + * 例如:Keep-Alive: 300 + * + * @param keepAlive keep-alive + * @return 返回当前对象 + + */ + public HttpHeaderBuilder keepAlive(String keepAlive) { + headerMaps.put(HttpReqHead.KEEP_ALIVE, + new BasicHeader(HttpReqHead.KEEP_ALIVE, keepAlive)); + return this; + } + + public String accept() { + return get(HttpReqHead.ACCEPT); + } + + public String acceptCharset() { + return get(HttpReqHead.ACCEPT_CHARSET); + } + + public String acceptEncoding() { + return get(HttpReqHead.ACCEPT_ENCODING); + } + + public String acceptLanguage() { + return get(HttpReqHead.ACCEPT_LANGUAGE); + } + + public String acceptRanges() { + return get(HttpReqHead.ACCEPT_RANGES); + } + + public String authorization() { + return get(HttpReqHead.AUTHORIZATION); + } + + public String cacheControl() { + return get(HttpReqHead.CACHE_CONTROL); + } + + public String connection() { + return get(HttpReqHead.CONNECTION); + } + + public String cookie() { + return get(HttpReqHead.COOKIE); + } + + public String contentLength() { + return get(HttpReqHead.CONTENT_LENGTH); + } + + public String contentType() { + return get(HttpReqHead.CONTENT_TYPE); + } + + public String date() { + return get(HttpReqHead.DATE); + } + + public String expect() { + return get(HttpReqHead.EXPECT); + } + + public String from() { + return get(HttpReqHead.FROM); + } + + public String host() { + return get(HttpReqHead.HOST); + } + + public String ifMatch() { + return get(HttpReqHead.IF_MATCH); + } + + public String ifModifiedSince() { + return get(HttpReqHead.IF_MODIFIED_SINCE); + } + + public String ifNoneMatch() { + return get(HttpReqHead.IF_NONE_MATCH); + } + + public String ifRange() { + return get(HttpReqHead.IF_RANGE); + } + + public String ifUnmodifiedSince() { + return get(HttpReqHead.IF_UNMODIFIED_SINCE); + } + + public String maxForwards() { + return get(HttpReqHead.MAX_FORWARDS); + } + + public String pragma() { + return get(HttpReqHead.PRAGMA); + } + + public String proxyAuthorization() { + return get(HttpReqHead.PROXY_AUTHORIZATION); + } + + public String referer() { + return get(HttpReqHead.REFERER); + } + + public String te() { + return get(HttpReqHead.TE); + } + + public String upgrade() { + return get(HttpReqHead.UPGRADE); + } + + public String userAgent() { + return get(HttpReqHead.USER_AGENT); + } + + public String via() { + return get(HttpReqHead.VIA); + } + + public String warning() { + return get(HttpReqHead.WARNING); + } + + public String keepAlive() { + return get(HttpReqHead.KEEP_ALIVE); + } + + + /** + * 获取head信息 + * + * @return + */ + private String get(String headName) { + if (headerMaps.containsKey(headName)) { + return headerMaps.get(headName).getValue(); + } + return null; + } + + /** + * 返回header头信息 + * + * @return 返回构建的header头信息数组 + */ + public Header[] build() { + Header[] headers = new Header[headerMaps.size()]; + int i = 0; + for (Header header : headerMaps.values()) { + headers[i] = header; + i++; + } + headerMaps.clear(); + headerMaps = null; + return headers; + } + + /** + * Http头信息 + * + * @author arron + * @date 2015年11月9日 上午11:29:04 + * @version 1.0 + */ + private static class HttpReqHead { + public static final String ACCEPT = "Accept"; + public static final String ACCEPT_CHARSET = "Accept-Charset"; + public static final String ACCEPT_ENCODING = "Accept-Encoding"; + public static final String ACCEPT_LANGUAGE = "Accept-Language"; + public static final String ACCEPT_RANGES = "Accept-Ranges"; + public static final String AUTHORIZATION = "Authorization"; + public static final String AC_PROPERTY_ID = "AC-Property-Id"; + public static final String CACHE_CONTROL = "Cache-Control"; + public static final String CONNECTION = "Connection"; + public static final String COOKIE = "Cookie"; + public static final String CONTENT_LENGTH = "Content-Length"; + public static final String CONTENT_TYPE = "Content-Type"; + public static final String DATE= "Date"; + public static final String EXPECT = "Expect"; + public static final String FROM = "From"; + public static final String HOST = "Host"; + public static final String IF_MATCH = "If-Match"; + public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; + public static final String IF_NONE_MATCH = "If-None-Match"; + public static final String IF_RANGE = "If-Range"; + public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; + public static final String KEEP_ALIVE = "Keep-Alive"; + public static final String MAX_FORWARDS = "Max-Forwards"; + public static final String PRAGMA = "Pragma"; + public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; + public static final String RANGE = "Range"; + public static final String REFERER = "Referer"; + public static final String TE = "TE"; + public static final String UPGRADE = "Upgrade"; + public static final String USER_AGENT = "User-Agent"; + public static final String VIA = "Via"; + public static final String WARNING = "Warning"; + } + + /** + * 常用头信息配置 + * + * @author arron + * @version 1.0 + */ + public static class Headers{ + public static final String APP_FORM_URLENCODED="application/x-www-form-urlencoded"; + public static final String TEXT_PLAIN="text/plain"; + public static final String TEXT_HTML="text/html"; + public static final String TEXT_XML="text/xml"; + public static final String TEXT_JSON="text/json"; + public static final String CONTENT_CHARSET_ISO_8859_1 = StandardCharsets.ISO_8859_1.name(); + public static final String CONTENT_CHARSET_UTF8 = StandardCharsets.UTF_8.name(); + public static final String DEF_PROTOCOL_CHARSET = StandardCharsets.US_ASCII.name(); + public static final String CONN_CLOSE = "close"; + public static final String KEEP_ALIVE = "keep-alive"; + public static final String EXPECT_CONTINUE = "100-continue"; + } + + + + + + public static Map getDefaultHeaders(URLConnection connection) { + Map defaultHeaders = new HashMap<>(); + defaultHeaders.put(HttpHeader.ACCEPT, "*/*"); + defaultHeaders.put(HttpHeader.CONNECTION, "Keep-Alive"); + defaultHeaders.put(HttpHeader.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + defaultHeaders.put(HttpHeader.ACCEPT_CHARSET, "utf-8"); + return defaultHeaders; + } + + /** + * 設置請求頭到連接對象 + * + * @param conn URL連接對象 + * @param contentType 內容類型 + * @param headers HTTP 請求頭 Map(可選,如果為 null 或空則使用默認值) + */ + public static void setRequestHeaders(URLConnection conn, String contentType, Map headers) { + Map defaultHeaders = getDefaultHeaders(conn); + // 如果傳入了自定義 headers,則合併(自定義的優先) + if (headers != null && !headers.isEmpty()) { + defaultHeaders.putAll(headers); + } + // 設置所有請求頭 + for (Map.Entry entry : defaultHeaders.entrySet()) { + conn.setRequestProperty(entry.getKey(), entry.getValue()); + } + // Content-Type 優先級最高,如果指定了則覆蓋 + if (contentType != null) { + conn.setRequestProperty("Content-Type", contentType); + } + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpParameterBuilder.java b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpParameterBuilder.java new file mode 100644 index 0000000..850f89f --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpParameterBuilder.java @@ -0,0 +1,72 @@ +package com.mgm.mcrm.apigateway.common.httpclient; + + +import com.alibaba.fastjson.JSON; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * HTTP 参数构建器 + * + * 用于构建 HTTP 请求参数(查询字符串、请求体等)。 + * + * @author Aaka + */ +public class HttpParameterBuilder { + private static final Logger log = LoggerFactory.getLogger(HttpParameterBuilder.class); + + /** + * 將參數 Map 轉換為查詢字符串(用於 GET 請求) + * + * @param params 參數 Map + * @return 查詢字符串,格式為 name1=value1&name2=value2 + */ + public static String buildQueryString(Map params) { + if (params == null || params.isEmpty()) { + return ""; + } + return params.entrySet().stream() + .filter(entry -> entry.getKey() != null && entry.getValue() != null) + .map(entry -> { + try { + String key = URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8); + String value = URLEncoder.encode(String.valueOf(entry.getValue()), StandardCharsets.UTF_8); + return key + "=" + value; + } catch (Exception e) { + log.warn("Failed to encode parameter: {}={}", entry.getKey(), entry.getValue(), e); + return null; + } + }) + .filter(java.util.Objects::nonNull) + .collect(Collectors.joining("&")); + } + + + /** + * 將參數 Map 轉換為請求體字符串 + * + * @param params 參數 Map + * @param contentType 內容類型 + * @return 請求體字符串 + */ + public static String buildRequestBody(Map params, String contentType) { + if (params == null || params.isEmpty()) { + return ""; + } + // 如果是 JSON 格式 + if (contentType != null && contentType.contains("application/json")) { + // 使用 FastJSON 將 Map 轉換為 JSON 字符串 + return JSON.toJSONString(params); + } else { + // 默認使用 form-urlencoded 格式 + return buildQueryString(params); + } + } + +} + diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpRequestOptions.java b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpRequestOptions.java new file mode 100644 index 0000000..016d23e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpRequestOptions.java @@ -0,0 +1,375 @@ +package com.mgm.mcrm.apigateway.common.httpclient; + +import com.mgm.mcrm.apigateway.common.enums.HttpMethods; +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.entity.mime.Header; +import org.apache.hc.core5.http.protocol.HttpContext; +import org.apache.hc.core5.util.Timeout; + +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; + +public class HttpRequestOptions { + + private HttpRequestOptions(){}; + + /** + * 获取实例 + * @return 返回当前对象 + */ + public static HttpRequestOptions create(){ + return new HttpRequestOptions(); + } + + + /** + * HttpClient对象 + */ + private HttpClient client; + + /** + * Header头信息 + */ + private Header[] headers; + + /** + * 是否返回response的headers + */ + private boolean isReturnRespHeaders; + + /** + * 请求方法 + */ + private HttpMethods method=HttpMethods.POST; + + /** + * 请求方法名称 + */ + private String methodName; + + /** + * 用于cookie操作 + */ + private HttpContext context; + + // private Map map; + + /** + * 以json格式作为输入参数 + */ + private String json; + + /** + * 输入输出编码 + */ + private String encoding= Charset.defaultCharset().displayName(); + + /** + * 输入编码 + */ + private String inenc; + + /** + * 输出编码 + */ + private String outenc; + + /** + * 设置RequestConfig + */ + private RequestConfig requestConfig; + + /** + * 解决多线程下载时,strean被close的问题 + */ + private static final ThreadLocal outs = new ThreadLocal(); + + /** + * 解决多线程处理时,url被覆盖问题 + */ + private static final ThreadLocal urls = new ThreadLocal(); + + /** + * 解决多线程处理时,url被覆盖问题 + */ + private static final ThreadLocal> maps = new ThreadLocal>(); + + /** + * @param client HttpClient对象 + * @return 返回当前对象 + */ + public HttpRequestOptions client(HttpClient client) { + this.client = client; + return this; + } + + /** + * @param url 资源url + * @return 返回当前对象 + */ + public HttpRequestOptions url(String url) { + urls.set(url); + return this; + } + + /** + * @param headers Header头信息 + * @return 返回当前对象 + */ + public HttpRequestOptions headers(Header[] headers) { + this.headers = headers; + return this; + } + + /** + * Header头信息(是否返回response中的headers) + * + * @param headers Header头信息 + * @param isReturnRespHeaders 是否返回response中的headers + * @return 返回当前对象 + */ + public HttpRequestOptions headers(Header[] headers, boolean isReturnRespHeaders) { + this.headers = headers; + this.isReturnRespHeaders=isReturnRespHeaders; + return this; + } + + /** + * @param method 请求方法 + * @return 返回当前对象 + */ + public HttpRequestOptions method(HttpMethods method) { + this.method = method; + return this; + } + + /** + * @param methodName 请求方法 + * @return 返回当前对象 + */ + public HttpRequestOptions methodName(String methodName) { + this.methodName = methodName; + return this; + } + + /** + * @param context cookie操作相关 + * @return 返回当前对象 + */ + public HttpRequestOptions context(HttpContext context) { + this.context = context; + return this; + } + + /** + * @param map 传递参数 + * @return 返回当前对象 + */ + public HttpRequestOptions map(Map map) { +// synchronized (getClass()) { +// if(this.map==null || map==null){ +// this.map = map; +// }else { +// this.map.putAll(map);; +// } +// } + Map m = maps.get(); + if(m == null || map.isEmpty()){ + m = map; + }else { + m.putAll(map); + } + maps.set(m); + return this; + } + + /** + * @param json 以json格式字符串作为参数 + * @return 返回当前对象 + */ + public HttpRequestOptions json(String json) { + this.json = json; + Map map = new HashMap(); + map.put(HttpEntityBuilder.ENTITY_JSON, json); + maps.set(map); + return this; + } + + /** + * @param filePaths 待上传文件所在路径 + * @return 返回当前对象 + */ + public HttpRequestOptions files(String[] filePaths) { + return files(filePaths, "file"); + } + /** + * 上传文件时用到 + * @param filePaths 待上传文件所在路径 + * @param inputName 即file input 标签的name值,默认为file + * @return 返回当前对象 + */ + public HttpRequestOptions files(String[] filePaths, String inputName) { + return files(filePaths, inputName, false); + } + /** + * 上传文件时用到 + * @param filePaths 待上传文件所在路径 + * @param inputName 即file input 标签的name值,默认为file + * @param forceRemoveContentTypeChraset 是否强制一处content-type中设置的编码类型 + * @return 返回当前对象 + */ + public HttpRequestOptions files(String[] filePaths, String inputName, boolean forceRemoveContentTypeChraset) { +// synchronized (getClass()) { +// if(this.map==null){ +// this.map= new HashMap(); +// } +// } +// map.put(Utils.ENTITY_MULTIPART, filePaths); +// map.put(Utils.ENTITY_MULTIPART+".name", inputName); +// map.put(Utils.ENTITY_MULTIPART+".rmCharset", forceRemoveContentTypeChraset); + + Map m = maps.get(); + if(m.isEmpty()){ + m = new HashMap(); + } + m.put(HttpEntityBuilder.ENTITY_MULTIPART, filePaths); + m.put(HttpEntityBuilder.ENTITY_MULTIPART+".name", inputName); + m.put(HttpEntityBuilder.ENTITY_MULTIPART+".rmCharset", forceRemoveContentTypeChraset); + maps.set(m); + return this; + } + + /** + * @param encoding 输入输出编码 + * @return 返回当前对象 + */ + public HttpRequestOptions encoding(String encoding) { + //设置输入输出 + inenc(encoding); + outenc(encoding); + this.encoding = encoding; + return this; + } + + /** + * @param inenc 输入编码 + */ + public void inenc(String inenc) { + this.inenc = inenc; + } + + /** + * @param outenc 输出编码 + */ + public void outenc(String outenc) { + this.outenc = outenc; + } + + /** + * @param out 输出流对象 + * @return 返回当前对象 + */ + public HttpRequestOptions out(OutputStream out) { + outs.set(out); + return this; + } + + /** + * 设置超时时间 + * + * @param timeout 超市时间,单位-毫秒 + * @return 返回当前对象 + */ + public HttpRequestOptions timeout(int timeout){ + return timeout(timeout, true); + } + + /** + * 设置超时时间以及是否允许网页重定向(自动跳转 302) + * + * @param timeout 超时时间,单位-毫秒 + * @param redirectEnable 自动跳转 + * @return 返回当前对象 + */ + public HttpRequestOptions timeout(int timeout, boolean redirectEnable){ + // 配置请求的超时设置 + // 注意:HttpClient 5 中需要使用 Timeout 对象,而不是直接的 int 值 + Timeout timeoutValue = Timeout.ofMilliseconds(timeout); + RequestConfig config = RequestConfig.custom() + .setConnectionRequestTimeout(timeoutValue) + .setResponseTimeout(timeoutValue) + .setRedirectsEnabled(redirectEnable) + .build(); + return timeout(config); + } + + /** + * 设置代理、超时时间、允许网页重定向等 + * + * @param requestConfig 超时时间,单位-毫秒 + * @return 返回当前对象 + */ + public HttpRequestOptions timeout(RequestConfig requestConfig){ + this.requestConfig = requestConfig; + return this; + } + + public HttpClient client() { + return client; + } + + public Header[] headers() { + return headers; + } + public boolean isReturnRespHeaders() { + return isReturnRespHeaders; + } + + public String url() { + return urls.get(); + } + + public HttpMethods method() { + return method; + } + + public String methodName() { + return methodName; + } + + public HttpContext context() { + return context; + } + + public Map map() { +// return map; + return maps.get(); + } + + public String json() { + return json; + } + + public String encoding() { + return encoding; + } + + public String inenc() { + return inenc == null ? encoding : inenc; + } + + public String outenc() { + return outenc == null ? encoding : outenc; + } + + public OutputStream out() { + return outs.get(); + } + + public RequestConfig requestConfig() { + return requestConfig; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpSslSupport.java b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpSslSupport.java new file mode 100644 index 0000000..b6e003d --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpSslSupport.java @@ -0,0 +1,197 @@ +package com.mgm.mcrm.apigateway.common.httpclient; + +import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; +import org.apache.hc.core5.ssl.SSLContexts; + +import javax.net.ssl.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; + +/** + * SSL 工具类(基于 HttpClient 5) + * + * 提供: + * - 信任所有证书的 SSLSocketFactory + * - 信任所有证书的 SSLConnectionSocketFactory + * + * 注意:默认实现信任所有证书,仅适用于内部系统或测试环境。 + * + * @author Anka + * @version 2.0 (client5) + */ +public class HttpSslSupport { + private static final SSLHandler SIMPLE_VERIFIER = new SSLHandler(); + + private static SSLSocketFactory sslFactory; + // 注意:SSLConnectionSocketFactory 在 HttpClient 5 中被标记为 deprecated, + // 但这是目前唯一可用的方式,新的 API 尚未稳定 + @SuppressWarnings("deprecation") + private static SSLConnectionSocketFactory sslConnFactory; + + private static final HttpSslSupport INSTANCE = new HttpSslSupport(); + + private SSLContext sc; + + public static HttpSslSupport getInstance() { + return INSTANCE; + } + + public static HttpSslSupport custom() { + return new HttpSslSupport(); + } + + /** + * 重写 X509TrustManager 和 HostnameVerifier,信任所有证书与主机名。 + * 仅推荐在内部环境或调试场景使用。 + */ + private static class SSLHandler implements X509TrustManager, HostnameVerifier { + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, + String authType) throws CertificateException { + // 信任所有服务器证书 + } + + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, + String authType) throws CertificateException { + // 信任所有客户端证书 + } + + @Override + public boolean verify(String paramString, SSLSession paramSSLSession) { + // 信任所有主机名 + return true; + } + } + + /** + * 获取默认 HostnameVerifier(信任所有主机名) + */ + public static HostnameVerifier getVerifier() { + return SIMPLE_VERIFIER; + } + + /** + * 获取信任所有证书的 {@link SSLSocketFactory} + */ + public synchronized SSLSocketFactory getSSLSF(SSLProtocolVersion sslpv) { + if (sslFactory != null) { + return sslFactory; + } + try { + SSLContext context = getSSLContext(sslpv); + context.init(null, new TrustManager[]{SIMPLE_VERIFIER}, null); + sslFactory = context.getSocketFactory(); + return sslFactory; + } catch (KeyManagementException e) { + throw new RuntimeException("初始化 SSLSocketFactory 失败", e); + } + } + + /** + * 获取信任所有证书的 {@link SSLConnectionSocketFactory}(HttpClient5) + * + * 注意:SSLConnectionSocketFactory 在 HttpClient 5 中被标记为 deprecated, + * 但这是目前唯一可用的方式,新的 API 尚未稳定 + */ + @SuppressWarnings("deprecation") + public synchronized SSLConnectionSocketFactory getSSLCONNSF(SSLProtocolVersion sslpv) { + if (sslConnFactory != null) { + return sslConnFactory; + } + try { + SSLContext context = getSSLContext(sslpv); + context.init(null, new TrustManager[]{SIMPLE_VERIFIER}, new java.security.SecureRandom()); + sslConnFactory = new SSLConnectionSocketFactory(context, SIMPLE_VERIFIER); + return sslConnFactory; + } catch (KeyManagementException e) { + throw new RuntimeException("初始化 SSLConnectionSocketFactory 失败", e); + } + } + + /** + * 使用自定义证书(trustStore)初始化 SSLContext + */ + public HttpSslSupport customSSL(String keyStorePath, String keyStorepass) { + FileInputStream instream = null; + try { + KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); + instream = new FileInputStream(new File(keyStorePath)); + trustStore.load(instream, keyStorepass.toCharArray()); + // 相信自己的 CA 和所有自签名证书 + this.sc = SSLContexts.custom() + .loadTrustMaterial(trustStore, (chain, authType) -> true) + .build(); + return this; + } catch (KeyManagementException | KeyStoreException | + NoSuchAlgorithmException | CertificateException | IOException e) { + throw new RuntimeException("初始化自定义 SSLContext 失败", e); + } finally { + if (instream != null) { + try { + instream.close(); + } catch (IOException ignore) { + // ignore + } + } + } + } + + /** + * 获取(或创建)指定协议版本的 {@link SSLContext} + */ + public SSLContext getSSLContext(SSLProtocolVersion sslpv) { + try { + if (this.sc == null) { + this.sc = SSLContext.getInstance(sslpv.getName()); + } + return this.sc; + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("不支持的 SSL 协议版本: " + sslpv.getName(), e); + } + } + + /** + * 支持的 SSL/TLS 协议版本枚举 + */ + public enum SSLProtocolVersion { + SSL("SSL"), + SSLv3("SSLv3"), + TLSv1("TLSv1"), + TLSv1_1("TLSv1.1"), + TLSv1_2("TLSv1.2"), + TLSv1_3("TLSv1.3"); + + private final String name; + + SSLProtocolVersion(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + public static SSLProtocolVersion find(String name) { + for (SSLProtocolVersion pv : SSLProtocolVersion.values()) { + if (pv.getName().equalsIgnoreCase(name)) { + return pv; + } + } + throw new IllegalArgumentException("未支持当前 SSL 版本号:" + name); + } + + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java b/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java new file mode 100644 index 0000000..e5d444b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java @@ -0,0 +1,33 @@ +package com.mgm.mcrm.apigateway.common.listener; + +import com.mgm.mcrm.apigateway.allotment.services.RoomCriteriaDaylightService; +import com.mgm.mcrm.apigateway.common.constants.SysCommon; +import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl.AvailableRoomCountUpdatedHandler; +import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl.ReservationNotificationHandler; +import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl.RoomTypeDateAvailabilityChangedHandler; +import com.mgm.mcrm.apigateway.daylight.webhook.services.DaylightWebhookFactory; +import com.mgm.mcrm.apigateway.reservation.services.BookingService; +import jakarta.annotation.Resource; +import org.springframework.boot.context.event.ApplicationStartedEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +@Component +public class ConfigListener implements ApplicationListener { + + @Resource + private RoomCriteriaDaylightService roomCriteriaDaylightService; + @Resource + private BookingService bookingService; + + @Override + public void onApplicationEvent(ApplicationStartedEvent event) { + + SysCommon.initRuntimePath(); + //--Register Allotment handlers + DaylightWebhookFactory.registerHandler("RoomTypeDateAvailabilityChanged", new RoomTypeDateAvailabilityChangedHandler(roomCriteriaDaylightService)); + DaylightWebhookFactory.registerHandler("AvailableRoomCountUpdated", new AvailableRoomCountUpdatedHandler(roomCriteriaDaylightService)); + //--Register Reservation Notification handlers + DaylightWebhookFactory.registerHandler("ReservationNotification", new ReservationNotificationHandler(bookingService)); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/utils/DateTimeUtil.java b/src/main/java/com/mgm/mcrm/apigateway/common/utils/DateTimeUtil.java new file mode 100644 index 0000000..917ec9a --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/utils/DateTimeUtil.java @@ -0,0 +1,169 @@ +package com.mgm.mcrm.apigateway.common.utils; + +import org.apache.commons.lang3.StringUtils; + +import java.text.SimpleDateFormat; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Date; + +public class DateTimeUtil { + + + public static LocalDateTime getDateFromString(String dateTimeValue, String dateFormat) { + if (StringUtils.isBlank(dateTimeValue)) { + return null; + } + + try { + DateTimeFormatter dtFormat = DateTimeFormatter.ofPattern(dateFormat); + + // 根据格式判断是纯日期还是包含时间 + if (dateFormat.contains("HH") || dateFormat.contains("mm") || dateFormat.contains("ss")) { + // 包含时间格式,使用LocalDateTime解析 + return LocalDateTime.parse(dateTimeValue, dtFormat); + } else { + // 纯日期格式,使用LocalDate解析后转换为LocalDateTime + LocalDate localDate = LocalDate.parse(dateTimeValue, dtFormat); + return localDate.atStartOfDay(); // 设置为当天的00:00:00 + } + } catch (Exception e) { + System.err.println("日期解析失败: " + e.getMessage()); + return null; + } + } + + public static String getDateByFormat(LocalDateTime dtDateTime, String strFormat) { + + String returnDate = null; + if (dtDateTime != null) { + try { + DateTimeFormatter dtFormat = DateTimeFormatter.ofPattern(strFormat); + returnDate = dtDateTime.format(dtFormat); + } catch (RuntimeException ignored) { + + } + } + return returnDate; + } + + + public static String getCurrentDate(String strFormat) { + return getDateByFormat(LocalDateTime.now(), strFormat); + } + + public static LocalDateTime getCurrentDateTime(String strFormat) { + + LocalDateTime returnDate = null; + try { + DateTimeFormatter dtFormat = DateTimeFormatter.ofPattern(strFormat); + String dateTimeValue = getDateByFormat(LocalDateTime.now(), strFormat); + returnDate = LocalDateTime.parse(dateTimeValue,dtFormat); + } catch (Exception ignored) { + + } + + return returnDate; + } + + public static LocalDateTime addDay(LocalDateTime currdate,long days) { + return currdate.plusDays(days); + } + + public static String getDateByFormatUseTimeStamp(Long timeStamp, String strFormat) { + + String returnDate = null; + + if (timeStamp != null) { + try { + SimpleDateFormat formatter; + formatter = new SimpleDateFormat(strFormat); + returnDate = formatter.format(timeStamp); + } catch (Exception err) { + + } + } + return returnDate; + } + + public static String getDiffDate(LocalDateTime DateStart, LocalDateTime DateEnd, String dateType) { + String returnDate = null; + try { + + Duration duration = Duration.between(DateStart, DateEnd); + //long diffTime = DateEnd.getTime() - DateStart.getTime(); + if("day".equalsIgnoreCase(dateType)) { + long days = duration.toDays(); + returnDate = Long.toString(days); + }else if("hour".equalsIgnoreCase(dateType)) { + long hours = duration.toHours(); + returnDate = Long.toString(hours); + }else { //minute + long minutes = duration.toMinutes(); + returnDate = Long.toString(minutes); + } + } catch (Exception err) { + + } + return returnDate; + } + public static boolean isValidDate(String dateStr) { + try { + dateStr=dateStr.split(" ")[0]; + String[] parts = dateStr.split("-"); + + int year = Integer.parseInt(parts[0]); + int month = Integer.parseInt(parts[1]); + int day = Integer.parseInt(parts[2]); + + + if (month < 1 || month > 12) { + return false; + } + + YearMonth yearMonth = YearMonth.of(year, month); + int maxDay = yearMonth.lengthOfMonth(); + + boolean isLeapYear = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); + if (month == 2 && isLeapYear) { + maxDay = 29; + } else if (month == 2) { + maxDay = 28; + } + + if (day < 1 || day > maxDay) { + return false; + } + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + LocalDate.parse(dateStr, formatter); + return true; + } catch (DateTimeParseException e) { + return false; + } + } + + public static long fromDateStringToLong(String inVal) { + LocalDateTime date = null; + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss:SSS"); + try { + date = LocalDateTime.parse(inVal,formatter); + } catch (Exception e) { + e.printStackTrace(); + } + return date!=null?date.toEpochSecond(ZoneOffset.UTC):0; + } + + public static long getTimeSpanSSS(LocalDateTime startDate, LocalDateTime endDate) { + DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss:SSS"); + String startStr = timeFormat.format(startDate); + String endStr = timeFormat.format(endDate); + + long startMS = fromDateStringToLong(startStr); + long endMS = fromDateStringToLong(endStr); + long returnMS = endMS-startMS; + return returnMS; + } + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/utils/LogUtil.java b/src/main/java/com/mgm/mcrm/apigateway/common/utils/LogUtil.java new file mode 100644 index 0000000..f396152 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/utils/LogUtil.java @@ -0,0 +1,68 @@ +package com.mgm.mcrm.apigateway.common.utils; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.alibaba.fastjson.JSONObject; + + +public class LogUtil { + private static String loggerName = LogUtil.class.getName(); + private static Logger logger = LoggerFactory.getLogger(loggerName); + + public static Logger getLogger(Class callClass){ + try + { + logger = LoggerFactory.getLogger(callClass.getName()); + }catch(Exception err){ + loggerName = LogUtil.class.getName(); + logger = LoggerFactory.getLogger(loggerName); + } + return logger; + } + + public static void info(Class callClass,String message) { + getLogger(callClass).info(message); + } + + public static void warn(Class callClass,String message) { + getLogger(callClass).warn(message); + } + + public static void error(Class callClass,String message) { + getLogger(callClass).error(message); + } + + public static String getObjectAttr(Object srcObj, String[] attrNames) { + String attrText = ""; + if(srcObj!=null && attrNames!=null) { + try { + JSONObject jsonObject = (JSONObject)JSONObject.toJSON(srcObj); + for(String attr:attrNames) { + attrText+=(attr+"="); + if(jsonObject.containsKey(attr)) { + attrText+=jsonObject.getString(attr); + } + attrText+=";"; + } + }catch(Exception ignored) { + + } + } + return attrText; + } + + public static String getFormatLog(String operationTag, String dataTag, boolean isSuccess, String resultTag,String message) { + String logMessage = ""; + String statusMsg = (resultTag==null?"":resultTag); + String errorMsg = (message==null?"":message); + logMessage = operationTag+(StringUtils.isBlank(dataTag)?"":" ["+dataTag+"]"); + if(isSuccess) { + logMessage += (" successed" + (StringUtils.isBlank(statusMsg)?"":" with ["+statusMsg+"]")); + }else { + logMessage += (" failed with error ["+errorMsg+"]"); + } + + return logMessage; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/utils/NetworkUtil.java b/src/main/java/com/mgm/mcrm/apigateway/common/utils/NetworkUtil.java new file mode 100644 index 0000000..ad09a7e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/utils/NetworkUtil.java @@ -0,0 +1,355 @@ +package com.mgm.mcrm.apigateway.common.utils; + +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.regex.Matcher; +import java.util.regex.Pattern; + +import com.mgm.mcrm.apigateway.common.constants.SysCommon; +import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringEscapeUtils; +import org.springframework.web.util.UriUtils; + +public class NetworkUtil { + private static final List interalIpPatterns = new ArrayList(); + private static final Pattern IPV4_PATTERN = + Pattern.compile("^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"); + private static final List INTERNAL_IP_PREFIXES = Arrays.asList( + "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." + ); + + public static void setInternalIpPattern(String patternConfig) { + interalIpPatterns.clear(); + String[] patternItems = patternConfig.split(";"); + for(String pItem:patternItems) { + String pVal = pItem.trim(); + if(StringUtils.isNotBlank(patternConfig)) { + interalIpPatterns.add(pVal); + } + } + } + + public static boolean isMatchInternal(String ipContent) { + boolean internalFlag = false; + if(StringUtils.isNotBlank(ipContent) && !interalIpPatterns.isEmpty()) { + String ipPart = "\\b(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\b"; + for(String ipItem:interalIpPatterns) { + if(ipItem.contains("*")) { + String itemPattern = ipItem.replace(".", "\\."); + itemPattern = itemPattern.replace("*", ipPart); + Pattern pt = Pattern.compile(itemPattern); + Matcher mt = pt.matcher(ipContent); + if(mt.find()) { + internalFlag=true; + break; + } + }else { + if(ipContent.contains(ipItem)) { + internalFlag=true; + break; + } + } + } + } + return internalFlag; + } + + /** + * Gets the client ip addr. + * + * @param request the request + * @return the client ip addr + */ + public static String getClientIpAddr(HttpServletRequest request) { + String ip = getIpFromHeaders(request); + if(ip==null){ + ip = request.getRemoteAddr(); + } + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; + } + + private static String getIpFromHeaders(HttpServletRequest request) { + String[] headerNames = { + "X-Real-IP", + "X-Forwarded-For", + "Proxy-Client-IP", + "WL-Proxy-Client-IP", + "HTTP_CLIENT_IP", + "HTTP_X_FORWARDED_FOR" + }; + boolean isContainForwardHeader=false; + for (String headerName : headerNames) { + String headerValue = request.getHeader(headerName); + if (headerValue != null && !headerValue.isEmpty()) { + isContainForwardHeader=true; + //LogUtil.info(NetworkUtil.class,"Check header["+headerName+"] of value["+headerValue+"]"); + String ip = extractValidIp(headerValue); + if (ip != null) { + return ip; + } + } + } + return (isContainForwardHeader?"INVALID_IP":null); + } + + private static String extractValidIp(String headerValue) { + if (headerValue.contains(",")) { + String[] ips = headerValue.split(","); + for(int i=ips.length-1;i>=0;i--){ + String ip = ips[i].trim(); + if (isValidPublicIp(ip)) { + return ip; + } + } + return isValidIp(ips[ips.length - 1].trim())?ips[ips.length - 1].trim():null; + } else { + String ip = headerValue.trim(); + return isValidIp(ip) ? ip : null; + } + } + + private static boolean isValidPublicIp(String ip) { + return isValidIp(ip) && !isInternalIp(ip); + } + + private static boolean isValidIp(String ip) { + return ip != null && !ip.isEmpty() && + !"unknown".equalsIgnoreCase(ip) && + IPV4_PATTERN.matcher(ip).matches(); + } + + private static boolean isInternalIp(String ip) { + if (ip == null) return false; + if (ip.equals("127.0.0.1") || ip.equals("0:0:0:0:0:0:0:1")) { + return true; + } + for (String prefix : INTERNAL_IP_PREFIXES) { + if (ip.startsWith(prefix)) { + return true; + } + } + return false; + } + + /** + * Gets the real ips. + * + * @return the real ips + */ + public static List getRealIps() { + List ips = new ArrayList(); + String localip = null;// 本地IP,如果没有配置外网IP则返回它 + String netip = null;// 外网IP + Enumeration netInterfaces; + try { + netInterfaces = NetworkInterface.getNetworkInterfaces(); + } catch (SocketException e) { + return null; + } + + InetAddress ip = null; + boolean finded = false;// 是否找到外网IP + while (netInterfaces.hasMoreElements() && !finded) { + NetworkInterface ni = netInterfaces.nextElement(); + Enumeration address = ni.getInetAddresses(); + while (address.hasMoreElements()) { + ip = address.nextElement(); + if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && !ip.getHostAddress().contains(":")) { + netip = ip.getHostAddress(); + ips.add(netip); + finded = true; + break; + } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() + && !ip.getHostAddress().contains(":")) {// 内网IP + localip = ip.getHostAddress(); + ips.add(localip); + } + } + } + return ips; + } + + /** + * Gets the real ip. + * + * @return the real ip + */ + public static String getRealIp() { + return getRealIps().iterator().next(); + } + + /** + * 获取访问者内网IP + * + * @return the server ip + */ + public static String getInternalIp() { + // 本地IP,如果没有配置外网IP则返回它 + String localip = null; + // 外网IP + String netip = null; + try { + Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces(); + InetAddress ip = null; + // 是否找到外网IP + boolean finded = false; + while (netInterfaces.hasMoreElements() && !finded) { + NetworkInterface ni = (NetworkInterface) netInterfaces.nextElement(); + Enumeration address = ni.getInetAddresses(); + while (address.hasMoreElements()) { + ip = (InetAddress) address.nextElement(); + if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) { + // 外网IP + netip = ip.getHostAddress(); + finded = true; + break; + } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() + && ip.getHostAddress().indexOf(":") == -1) { + // 内网IP + localip = ip.getHostAddress(); + } + } + } + } catch (SocketException e) { + e.printStackTrace(); + } + + if (netip != null && !"".equals(netip)) { + return netip; + } else { + return localip; + } + } + + /** + * 判断ip是否在网段cidr内,cidr格式192.168.0.1/255 + * + * @return the server ip + */ + public static boolean ipInRange(String ip, String cidr) { + try { + String[] tmp = cidr.split("/"); + + if (tmp.length > 1) { + + String[] ips = ip.split("\\."); + int ipAddr = (Integer.parseInt(ips[0]) << 24) | (Integer.parseInt(ips[1]) << 16) + | (Integer.parseInt(ips[2]) << 8) | Integer.parseInt(ips[3]); + int type = Integer.parseInt(cidr.replaceAll(".*/", "")); + int mask = 0xFFFFFFFF << (32 - type); + String cidrIp = cidr.replaceAll("/.*", ""); + String[] cidrIps = cidrIp.split("\\."); + int cidrIpAddr = (Integer.parseInt(cidrIps[0]) << 24) | (Integer.parseInt(cidrIps[1]) << 16) + | (Integer.parseInt(cidrIps[2]) << 8) | Integer.parseInt(cidrIps[3]); + return (ipAddr & mask) == (cidrIpAddr & mask); + } else if (tmp.length == 1 && ip.equals(cidr)) { + return true; + } else { + return false; + } + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + } + + public static String getServerName() { + String serverName=""; + try { + InetAddress localhost = InetAddress.getLocalHost(); + serverName = localhost.getHostName(); + } catch (Exception e) { + e.printStackTrace(); + } + return serverName; + } + + /** + * 判断ip是否在网段cidr内,cidr格式192.168.0.1/255 + * + * @return the server ip + */ + public static boolean isMacauIp(String ip,String macauIps) { + try { + String[] cidrArr = StringUtils.split(macauIps,";"); + if(cidrArr.length>0) { + for(String cidr : cidrArr) { + String[] tmp = cidr.split("/"); + if (tmp.length > 1) { + String[] ips = ip.split("\\."); + try { + Integer.parseInt(ips[0]); + Integer.parseInt(ips[1]); + Integer.parseInt(ips[2]); + Integer.parseInt(ips[3]); + } catch (Exception e) { + continue; + } + int ipAddr = (Integer.parseInt(ips[0]) << 24) | (Integer.parseInt(ips[1]) << 16) + | (Integer.parseInt(ips[2]) << 8) | Integer.parseInt(ips[3]); + int type = Integer.parseInt(cidr.replaceAll(".*/", "")); + int mask = 0xFFFFFFFF << (32 - type); + String cidrIp = cidr.replaceAll("/.*", ""); + String[] cidrIps = cidrIp.split("\\."); + int cidrIpAddr = (Integer.parseInt(cidrIps[0]) << 24) | (Integer.parseInt(cidrIps[1]) << 16) + | (Integer.parseInt(cidrIps[2]) << 8) | Integer.parseInt(cidrIps[3]); + if((ipAddr & mask) == (cidrIpAddr & mask)) return true; + } else if (tmp.length == 1 && ip.equals(cidr)) { + return true; + } + } + }else { + return false; + } + + } catch (Exception ex) { + ex.printStackTrace(); + return false; + } + return false; + } + + public static boolean isValidURL(String requestUrl){ + try { + URI uri = new URI(requestUrl); + String scheme = uri.getScheme(); + String host = uri.getHost(); + if(!"https".equals(scheme) && !"http".equals(scheme)) { + return false; + } + if(host.matches("\\d+\\.\\d+\\.\\d+\\.\\d+")) { + return false; + } + return true; + } catch (Exception e) { + return false; + } + } + + public static String encodeParaWithSpring(String value) { + if(value==null) + value=""; + value= value.replace("\r", "").replace("\n", ""); + return UriUtils.encode(value, StandardCharsets.UTF_8); + } + + public static String sanitizeHeaderValue(String headerValue) { + return headerValue==null? null :(StringEscapeUtils.escapeJava( + headerValue.replace("\r", "").replace("\n", "") + )); + } + + public static String encodePathWithSpring(String value) { + if(value==null) + value=""; + value= value.replace("\r", "").replace("\n", ""); + return UriUtils.encodePath(value, StandardCharsets.UTF_8); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/utils/mockup/HttpUtilMockup.java b/src/main/java/com/mgm/mcrm/apigateway/common/utils/mockup/HttpUtilMockup.java new file mode 100644 index 0000000..db5fbde --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/common/utils/mockup/HttpUtilMockup.java @@ -0,0 +1,261 @@ +package com.mgm.mcrm.apigateway.common.utils.mockup; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import com.mgm.mcrm.apigateway.common.constants.SysCommon; +import com.mgm.mcrm.apigateway.common.utils.LogUtil; + +public class HttpUtilMockup { + private static JSONArray mockupConfig=null; + private static JSONArray getMockupConfig() { + if(mockupConfig==null) { + try { + String fileContent = SysCommon.loadResFileContentByPath("/mockup/mgm_api_mockup_def.json",1); + LogUtil.info(HttpUtilMockup.class, "[HttpUtilMockup] config file content length=" + (fileContent == null ? 0 : fileContent.length()) + ", first 100 chars=" + (fileContent == null ? "null" : fileContent.substring(0, Math.min(100, fileContent.length())))); + mockupConfig = JSONArray.parseArray(fileContent); + if(mockupConfig==null) { + LogUtil.error(HttpUtilMockup.class, "[HttpUtilMockup] config parsed to null, using empty array"); + mockupConfig = new JSONArray(); + } else { + LogUtil.info(HttpUtilMockup.class, "[HttpUtilMockup] config loaded successfully, items count=" + mockupConfig.size()); + } + }catch(Exception err) { + LogUtil.error(HttpUtilMockup.class, "[HttpUtilMockup] failed to load config: " + err.getMessage()); + mockupConfig = new JSONArray(); + } + } + return mockupConfig; + } + + public static String findMockResultByLink(String requestLink,String requestContent) { + String result =""; + JSONArray configData = getMockupConfig(); + LogUtil.info(HttpUtilMockup.class, "[HttpUtilMockup] findMockResultByLink requestLink=" + requestLink + ", requestContent=" + (requestContent == null ? "" : requestContent.substring(0, Math.min(requestContent.length(), 300)))); + LogUtil.info(HttpUtilMockup.class, "[HttpUtilMockup] configData size=" + configData.size()); + for(int i=0;i operateList = new ArrayList(); + String[] andList = condiction.split("&&"); + for(int i=0;i0) { + operateList.add("&&"); + } + operateList.add(andList[i].trim()); + } + for(int i=0;i0) { + operateList.add(i,"||"); + i++; + } + if(j==0) { + operateList.set(i, orList[j].trim()); + i++; + }else { + operateList.add(i, orList[j].trim()); + i++; + } + } + } + } + String lastOperate = "AND"; + boolean isFirst =true; + for(int i=0;i0) { + int operIndex = express.indexOf("=="); + operate="=="; + valName1 = express.substring(0, operIndex).trim(); + valName2 =express.substring(operIndex+2).trim(); + }else if(express.indexOf(">=")>0) { + int operIndex = express.indexOf(">="); + operate=">="; + valName1 = express.substring(0, operIndex).trim(); + valName2 =express.substring(operIndex+2).trim(); + }else if(express.indexOf("<=")>0) { + int operIndex = express.indexOf("<="); + operate="<="; + valName1 = express.substring(0, operIndex).trim(); + valName2 =express.substring(operIndex+2).trim(); + }else if(express.indexOf(">")>0) { + int operIndex = express.indexOf(">"); + operate=">"; + valName1 = express.substring(0, operIndex).trim(); + valName2 =express.substring(operIndex+1).trim(); + }else if(express.indexOf("<")>0) { + int operIndex = express.indexOf("<"); + operate="<"; + valName1 = express.substring(0, operIndex).trim(); + valName2 =express.substring(operIndex+1).trim(); + } + if(!operate.isEmpty()) { + double numVal1 =0; + double numVal2 =0; + boolean isNum=true; + if(requestObj.containsKey(valName1)) { + try { + numVal1 = requestObj.getDoubleValue(valName1); + }catch(Exception err) { + isNum=false; + } + }else { + try { + numVal1 = Double.parseDouble(valName1); + }catch(Exception err) { + isNum=false; + } + } + if(isNum==true) { + if(requestObj.containsKey(valName2)) { + try { + numVal2 = requestObj.getDoubleValue(valName2); + }catch(Exception err) { + isNum=false; + } + }else { + try { + numVal2 = Double.parseDouble(valName2); + }catch(Exception err) { + isNum=false; + } + } + } + if(isNum==true) { + if(operate.equals("==")) { + result = (numVal1==numVal2); + }else if(operate.equals("<=")) { + result = (numVal1<=numVal2); + }else if(operate.equals(">=")) { + result = (numVal1>=numVal2); + }else if(operate.equals("<")) { + result = (numVal1")) { + result = (numVal1>numVal2); + } + }else { + String textVal1 = valName1; + String textVal2 = valName2; + if(requestObj.containsKey(valName1)) { + textVal1 = requestObj.getString(valName1); + } + if(requestObj.containsKey(valName2)) { + textVal2 = requestObj.getString(valName2); + } + if(operate.equals("==")) { + result = (textVal1.compareTo(textVal2)==0); + }else if(operate.equals("<=")) { + result = (textVal1.compareTo(textVal2)<=0); + }else if(operate.equals(">=")) { + result = (textVal1.compareTo(textVal2)>=0); + }else if(operate.equals("<")) { + result = (textVal1.compareTo(textVal2)<0); + }else if(operate.equals(">")) { + result = (textVal1.compareTo(textVal2)>0); + } + } + } + return result; + } + + public static String processMockRequest(String requestUrl, String requestContent) { + String responseEntity = findMockResultByLink(requestUrl, requestContent); + LogUtil.info(HttpUtilMockup.class, "[HttpUtilMockup] requestUrl=" + requestUrl + " mockResponse=" + (responseEntity == null ? "" : responseEntity.substring(0, Math.min(responseEntity.length(), 200)))); + return responseEntity; + } + + public static void clearConfigCache() { + mockupConfig = null; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/controllers/DaylightTestController.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/controllers/DaylightTestController.java new file mode 100644 index 0000000..7dd7c31 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/controllers/DaylightTestController.java @@ -0,0 +1,27 @@ +package com.mgm.mcrm.apigateway.daylight.common.controllers; + +import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; +import com.mgm.mcrm.apigateway.daylight.common.services.DaylightFetchConfigurationService; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class DaylightTestController { + + @Resource + private DaylightFetchConfigurationService daylightFetchConfigurationService; + + @GetMapping("/test/fetch-daylight-config") + public BaseApiResponse testFetchDaylightConfiguration() { + BaseApiResponse propertiesResponse = daylightFetchConfigurationService.fetchDaylightProperties(); + if (!propertiesResponse.isSuccess()) { + return propertiesResponse; + } + BaseApiResponse configResponse = daylightFetchConfigurationService.fetchDaylightConfiguration(); + if (!configResponse.isSuccess()) { + return configResponse; + } + return daylightFetchConfigurationService.fetchDaylightBlockCodes(); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/dtos/CachedToken.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/dtos/CachedToken.java new file mode 100644 index 0000000..e3e9114 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/dtos/CachedToken.java @@ -0,0 +1,12 @@ +package com.mgm.mcrm.apigateway.daylight.common.dtos; + +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class CachedToken { + private TokenResponse token; + private LocalDateTime expireTime; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/dtos/TokenResponse.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/dtos/TokenResponse.java new file mode 100644 index 0000000..8a365b1 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/dtos/TokenResponse.java @@ -0,0 +1,18 @@ +package com.mgm.mcrm.apigateway.daylight.common.dtos; + +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.Data; + +@Data +public class TokenResponse { + @JSONField(name = "access_token") + private String accessToken; + @JSONField(name = "expires_in") + private String expiresIn; + @JSONField(name = "token_type") + private String tokenType; + @JSONField(name = "refresh_token") + private String refreshToken; + @JSONField(name = "scope") + private String scope; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/scheduler/DaylightConfigurationScheduler.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/scheduler/DaylightConfigurationScheduler.java new file mode 100644 index 0000000..7c3bc90 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/scheduler/DaylightConfigurationScheduler.java @@ -0,0 +1,120 @@ +package com.mgm.mcrm.apigateway.daylight.common.scheduler; + +import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; +import com.mgm.mcrm.apigateway.daylight.common.services.DaylightFetchConfigurationService; +import com.mgm.mcrm.apigateway.common.utils.LogUtil; +import com.mgm.mcrm.apigateway.daylight.repositories.CfgDaylightConfigurationDao; +import jakarta.annotation.Resource; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.annotation.Order; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +public class DaylightConfigurationScheduler { + + @Resource + private DaylightFetchConfigurationService daylightFetchConfigurationService; + @Resource + private CfgDaylightConfigurationDao cfgDaylightConfigurationDao; + + @Order(1) + @EventListener(ApplicationReadyEvent.class) + public void fetchDaylightPropertiesOnStartupIfMissing() { + LogUtil.info(DaylightConfigurationScheduler.class, "Checking saved Daylight properties on startup"); + try { + if (cfgDaylightConfigurationDao.existsSavedDaylightProperties()) { + LogUtil.info(DaylightConfigurationScheduler.class, "Saved Daylight properties found, skip startup fetch"); + return; + } + + LogUtil.warn(DaylightConfigurationScheduler.class, "No saved Daylight properties found, start startup fetch"); + BaseApiResponse propResponse = daylightFetchConfigurationService.fetchDaylightProperties(); + if (propResponse.isSuccess()) { + LogUtil.info(DaylightConfigurationScheduler.class, "Startup properties completed: " + propResponse.getMessage()); + } else { + LogUtil.warn(DaylightConfigurationScheduler.class, "Startup properties failed: " + propResponse.getMessage()); + } + } catch (Exception e) { + LogUtil.error(DaylightConfigurationScheduler.class, "Startup properties check error: " + e.getMessage()); + } + } + + @Order(2) + @EventListener(ApplicationReadyEvent.class) + public void fetchDaylightConfigurationOnStartupIfMissing() { + LogUtil.info(DaylightConfigurationScheduler.class, "Checking saved Daylight configurations on startup"); + try { + BaseApiResponse configResponse = daylightFetchConfigurationService.fetchDaylightConfiguration(); + if (configResponse.isSuccess()) { + LogUtil.info(DaylightConfigurationScheduler.class, "Startup configurations completed: " + configResponse.getMessage()); + } else { + LogUtil.warn(DaylightConfigurationScheduler.class, "Startup configurations failed: " + configResponse.getMessage()); + } + } catch (Exception e) { + LogUtil.error(DaylightConfigurationScheduler.class, "Startup configurations check error: " + e.getMessage()); + } + } + + @Order(3) + @EventListener(ApplicationReadyEvent.class) + public void fetchDaylightBlockCodesOnStartupIfMissing() { + LogUtil.info(DaylightConfigurationScheduler.class, "Checking saved Daylight block codes on startup"); + try { + if (!cfgDaylightConfigurationDao.existsSavedDaylightProperties()) { + LogUtil.info(DaylightConfigurationScheduler.class, "No saved Daylight properties found, skip block codes startup fetch"); + return; + } + + BaseApiResponse blockResponse = daylightFetchConfigurationService.fetchDaylightBlockCodes(); + if (blockResponse.isSuccess()) { + LogUtil.info(DaylightConfigurationScheduler.class, "Startup block codes completed: " + blockResponse.getMessage()); + } else { + LogUtil.warn(DaylightConfigurationScheduler.class, "Startup block codes failed: " + blockResponse.getMessage()); + } + } catch (Exception e) { + LogUtil.error(DaylightConfigurationScheduler.class, "Startup block codes check error: " + e.getMessage()); + } + } + + /** + * 每天凌晨2点执行一次,拉取 Daylight 配置数据 + */ + @Scheduled(cron = "0 0 2 * * ?") + public void fetchDaylightConfigurationDaily() { + LogUtil.info(DaylightConfigurationScheduler.class, "Start scheduled task: fetchDaylightAllData"); + try { + // 1. 拉取 Properties + LogUtil.info(DaylightConfigurationScheduler.class, "Step 1/3: Fetching properties..."); + BaseApiResponse propResponse = daylightFetchConfigurationService.fetchDaylightProperties(); + if (propResponse.isSuccess()) { + LogUtil.info(DaylightConfigurationScheduler.class, "Properties completed: " + propResponse.getMessage()); + } else { + LogUtil.warn(DaylightConfigurationScheduler.class, "Properties failed: " + propResponse.getMessage()); + } + + // 2. 拉取 Configuration + LogUtil.info(DaylightConfigurationScheduler.class, "Step 2/3: Fetching configurations..."); + BaseApiResponse configResponse = daylightFetchConfigurationService.fetchDaylightConfiguration(); + if (configResponse.isSuccess()) { + LogUtil.info(DaylightConfigurationScheduler.class, "Configurations completed: " + configResponse.getMessage()); + } else { + LogUtil.warn(DaylightConfigurationScheduler.class, "Configurations failed: " + configResponse.getMessage()); + } + + // 3. 拉取 Block Codes + LogUtil.info(DaylightConfigurationScheduler.class, "Step 3/3: Fetching block codes..."); + BaseApiResponse blockResponse = daylightFetchConfigurationService.fetchDaylightBlockCodes(); + if (blockResponse.isSuccess()) { + LogUtil.info(DaylightConfigurationScheduler.class, "Block codes completed: " + blockResponse.getMessage()); + } else { + LogUtil.warn(DaylightConfigurationScheduler.class, "Block codes failed: " + blockResponse.getMessage()); + } + + LogUtil.info(DaylightConfigurationScheduler.class, "Scheduled task finished: fetchDaylightAllData"); + } catch (Exception e) { + LogUtil.error(DaylightConfigurationScheduler.class, "Scheduled task error: " + e.getMessage()); + } + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightFetchConfigurationService.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightFetchConfigurationService.java new file mode 100644 index 0000000..a49a672 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightFetchConfigurationService.java @@ -0,0 +1,651 @@ +package com.mgm.mcrm.apigateway.daylight.common.services; + +import com.alibaba.fastjson.JSONObject; +import com.mgm.mcrm.apigateway.basesetting.property.models.CfgProperties; +import com.mgm.mcrm.apigateway.basesetting.property.repositories.PropertyDao; +import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; +import com.mgm.mcrm.apigateway.common.dtos.config.DaylightApiSetting; +import com.mgm.mcrm.apigateway.daylight.common.dtos.TokenResponse; +import com.mgm.mcrm.apigateway.daylight.models.CfgDaylightConfiguration; +import com.mgm.mcrm.apigateway.daylight.models.SysLogDaylightConfigurationSync; +import com.mgm.mcrm.apigateway.daylight.repositories.CfgDaylightConfigurationDao; +import com.mgm.mcrm.apigateway.daylight.repositories.SysLogDaylightConfigurationSyncDao; +import com.mgm.mcrm.apigateway.proxy.exception.HttpProxyErrorHandler; +import com.mgm.mcrm.apigateway.proxy.services.WebProxyService; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +@Service +public class DaylightFetchConfigurationService { + + private static final Logger log = LoggerFactory.getLogger(DaylightFetchConfigurationService.class); + + private final HttpProxyErrorHandler httpErrorHandler = new HttpProxyErrorHandler(); + @Resource + private WebProxyService webProxyService; + @Resource + private DaylightTokenService daylightTokenService; + @Autowired + private DaylightApiSetting daylightApiSetting; + @Resource + private PropertyDao propertyDao; + @Resource + private CfgDaylightConfigurationDao cfgDaylightConfigurationDao; + @Resource + private SysLogDaylightConfigurationSyncDao syncLogDao; + + public BaseApiResponse fetchDaylightConfiguration() { + BaseApiResponse response = new BaseApiResponse(); + Date syncStartTime = new Date(); + + try { + TokenResponse daylightApiToken = daylightTokenService.getApiAccessToken(); + if (daylightApiToken == null || StringUtils.isBlank(daylightApiToken.getAccessToken())) { + response.setSuccess(false); + response.setMessage("Invoke daylight token failed"); + return response; + } + + List allProperties = propertyDao.findByCategory("DAYLIGHT"); + int savedCount = 0; + + if (allProperties == null || allProperties.isEmpty()) { + response.setSuccess(false); + response.setMessage("No properties found with category 'DAYLIGHT'"); + return response; + } + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); + + for (CfgProperties property : allProperties) { + String dlPropertyId = property.getDlPropertyId(); + if (StringUtils.isBlank(dlPropertyId)) { + continue; + } + + // 按每个 property 查询上次成功同步时间 + // 注意:仅当 sys_log 有 SUCCESS 日志,且 cfg_daylight_configuration 表中确实存在该 property 的 + // 配置数据时,才走增量同步;否则强制走全量同步,避免数据空缺时漏掉 configType + String updatedAtParam = ""; + Optional lastSync = syncLogDao + .findTopByConfigNameAndDlPropertyIdAndSyncStatusOrderByLastSyncAtDesc( + "Configuration", dlPropertyId, "SUCCESS"); + boolean hasSavedConfig = cfgDaylightConfigurationDao.existsSavedConfigurationByDlPropertyId(dlPropertyId); + if (lastSync.isPresent() && hasSavedConfig) { + updatedAtParam = "&updatedAt=" + sdf.format(lastSync.get().getLastSyncAt()); + log.info("Property {} incremental sync, updatedAt: {}", dlPropertyId, updatedAtParam); + } else { + if (lastSync.isPresent() && !hasSavedConfig) { + log.warn("Property {} sync log found but config data missing, fallback to full sync", dlPropertyId); + } else { + log.info("Property {} first sync, no updatedAt parameter", dlPropertyId); + } + } + + // 每个 property 先删除旧配置,再重新拉取 + Map> accumulatedResults = new HashMap<>(); + + String path = "/api-gateway/aggregator/v1/configuration/extract"; + String apiUrl = daylightApiSetting.getServerUrl() + path; + String types = "PurchaseElement,MarketSegment,RatePlan,BookingSource,MembershipLevel," + + "Attribute,RoomType,GuaranteeType,DistributionChannel,AgeBucket,CancellationReason," + + "ComplimentaryAccountingPreset,ComplimentaryType,TransactionCode"; + + String pagingNext; + int pageNumber = 1; + boolean fetchSuccess = true; + + do { + HttpHeaders headers = new HttpHeaders(); + headers.set("authorization", "Bearer " + daylightApiToken.getAccessToken()); + headers.set("Ac-Property-Id", dlPropertyId); + + String queryString = "types=" + types + "&pageSize=500&pageNumber=" + pageNumber + updatedAtParam; + log.info("Fetching page {} for Ac-Property-Id: {}", pageNumber, dlPropertyId); + + ResponseEntity requestResponse = webProxyService.invokeRequest( + WebProxyService.DAYLIGHT_API, + HttpMethod.GET, + apiUrl, + queryString, + headers, + new byte[0], + Duration.ofSeconds(daylightApiSetting.getApiTimeout()), + httpErrorHandler + ).block(); + + if (requestResponse == null) { + log.warn("Property {} configuration fetch failed: Http request server can not visit", dlPropertyId); + fetchSuccess = false; + break; + } + + response.setStatusCode(requestResponse.getStatusCode().value()); + + if (!requestResponse.getStatusCode().is2xxSuccessful()) { + byte[] errorBody = requestResponse.getBody(); + String errorMsg = errorBody != null ? new String(errorBody, StandardCharsets.UTF_8) : "Request failed"; + log.warn("Property {} configuration fetch failed: {}", dlPropertyId, errorMsg); + fetchSuccess = false; + break; + } + + byte[] responseBody = requestResponse.getBody(); + if (responseBody == null) { + break; + } + + String jsonStr = new String(responseBody, StandardCharsets.UTF_8); + JSONObject responseJson = JSONObject.parseObject(jsonStr); + + // 累积 results 中每种 configType 的数据 + JSONObject results = responseJson.getJSONObject("results"); + if (results != null) { + for (String configType : results.keySet()) { + List pageItems = results.getJSONArray(configType).toJavaList(JSONObject.class); + accumulatedResults.computeIfAbsent(configType, k -> new ArrayList<>()).addAll(pageItems); + } + } + + // 解析 paging.next,判断是否有下一页 + pagingNext = null; + JSONObject paging = responseJson.getJSONObject("paging"); + if (paging != null && paging.containsKey("next") && StringUtils.isNotBlank(paging.getString("next"))) { + pagingNext = paging.getString("next"); + pageNumber++; + } + } while (pagingNext != null); + + // 将累积的结果保存为 CfgDaylightConfiguration + boolean isIncremental = StringUtils.isNotBlank(updatedAtParam); + for (Map.Entry> entry : accumulatedResults.entrySet()) { + String configType = entry.getKey(); + List newItems = entry.getValue(); + + if (isIncremental) { + // 增量同步:合并新旧数据 + CfgDaylightConfiguration existing = cfgDaylightConfigurationDao + .findByDlPropertyIdAndConfigName(dlPropertyId, configType); + if (existing != null && StringUtils.isNotBlank(existing.getConfigValue())) { + // 解析旧数据,用 id 建索引 + List oldItems = JSONObject.parseArray(existing.getConfigValue(), JSONObject.class); + Map mergedMap = new HashMap<>(); + for (JSONObject item : oldItems) { + mergedMap.put(getConfigItemKey(item), item); + } + logConfigurationChanges("Configuration incremental sync", dlPropertyId, configType, oldItems, newItems); + // 用新数据覆盖/新增 + for (JSONObject item : newItems) { + mergedMap.put(getConfigItemKey(item), item); + } + // 保存合并后的结果 + existing.setConfigValue(JSONObject.toJSONString(new ArrayList<>(mergedMap.values()))); + existing.setUpdatedAt(new Date()); + cfgDaylightConfigurationDao.save(existing); + log.info("Merged {} for {}: old={}, new={}, merged={}", + configType, dlPropertyId, oldItems.size(), newItems.size(), mergedMap.size()); + } else { + // DB 中没有旧记录,直接保存新数据 + CfgDaylightConfiguration config = new CfgDaylightConfiguration(); + config.setConfigName(configType); + config.setConfigValue(JSONObject.toJSONString(newItems)); + config.setDlPropertyId(dlPropertyId); + config.setCreatedAt(new Date()); + config.setUpdatedAt(new Date()); + cfgDaylightConfigurationDao.save(config); + logConfigurationCreated("Configuration incremental sync created", dlPropertyId, configType, newItems); + } + } else { + // 全量同步:直接保存 + CfgDaylightConfiguration existing = cfgDaylightConfigurationDao + .findByDlPropertyIdAndConfigName(dlPropertyId, configType); + List oldItems = existing != null && StringUtils.isNotBlank(existing.getConfigValue()) + ? JSONObject.parseArray(existing.getConfigValue(), JSONObject.class) + : new ArrayList<>(); + CfgDaylightConfiguration config = existing != null ? existing : new CfgDaylightConfiguration(); + config.setConfigName(configType); + config.setConfigValue(JSONObject.toJSONString(newItems)); + config.setDlPropertyId(dlPropertyId); + if (config.getCreatedAt() == null) { + config.setCreatedAt(new Date()); + } + config.setUpdatedAt(new Date()); + cfgDaylightConfigurationDao.save(config); + logConfigurationChanges("Configuration full sync", dlPropertyId, configType, oldItems, newItems); + } + savedCount++; + } + + // 按 property 记录同步日志 + SysLogDaylightConfigurationSync syncLog = new SysLogDaylightConfigurationSync(); + syncLog.setConfigName("Configuration"); + syncLog.setDlPropertyId(dlPropertyId); + syncLog.setLastSyncAt(syncStartTime); + syncLog.setSyncStatus(fetchSuccess ? "SUCCESS" : "FAILED"); + syncLog.setRecordCount(accumulatedResults.size()); + syncLog.setCreatedAt(new Date()); + syncLogDao.save(syncLog); + } + + response.setSuccess(true); + response.setMessage("Successfully fetched and saved " + savedCount + " configuration entries."); + } catch (Exception e) { + response.setSuccess(false); + response.setMessage(e.getMessage()); + } + return response; + } + + /** + * 从 Daylight API 拉取 properties 列表,存入 cfg_daylight_configuration 表 + */ + public BaseApiResponse fetchDaylightProperties() { + BaseApiResponse response = new BaseApiResponse(); + Date syncStartTime = new Date(); + + try { + TokenResponse daylightApiToken = daylightTokenService.getApiAccessToken(); + if (daylightApiToken == null || StringUtils.isBlank(daylightApiToken.getAccessToken())) { + response.setSuccess(false); + response.setMessage("Invoke daylight token failed"); + return response; + } + + String path = "/api-gateway/configuration/v1/properties"; + String apiUrl = daylightApiSetting.getServerUrl() + path; + int pageNumber = 1; + List allResults = new ArrayList<>(); + String pagingNext = null; + + do { + HttpHeaders headers = new HttpHeaders(); + headers.set("authorization", "Bearer " + daylightApiToken.getAccessToken()); + + String queryString = "pageSize=500&statusCode=Published&recursive=true&isActive=true&pageNumber=" + pageNumber; + log.info("Fetching properties page {}", pageNumber); + + ResponseEntity requestResponse = webProxyService.invokeRequest( + WebProxyService.DAYLIGHT_API, + HttpMethod.GET, + apiUrl, + queryString, + headers, + new byte[0], + Duration.ofSeconds(daylightApiSetting.getApiTimeout()), + httpErrorHandler + ).block(); + + if (requestResponse == null) { + response.setSuccess(false); + response.setMessage("Http request server can not visit."); + return response; + } + + response.setStatusCode(requestResponse.getStatusCode().value()); + + if (!requestResponse.getStatusCode().is2xxSuccessful()) { + byte[] errorBody = requestResponse.getBody(); + response.setSuccess(false); + response.setMessage(errorBody != null ? new String(errorBody, StandardCharsets.UTF_8) : "Request failed"); + return response; + } + + byte[] responseBody = requestResponse.getBody(); + if (responseBody == null) { + break; + } + + String jsonStr = new String(responseBody, StandardCharsets.UTF_8); + JSONObject responseJson = JSONObject.parseObject(jsonStr); + + // 累积 results 数组 + List pageResults = responseJson.getJSONArray("results").toJavaList(JSONObject.class); + if (pageResults != null) { + allResults.addAll(pageResults); + } + + // 判断是否有下一页 + pagingNext = null; + JSONObject paging = responseJson.getJSONObject("paging"); + if (paging != null) { + int totalCount = paging.getIntValue("totalCount"); + int pageSize = paging.getIntValue("pageSize"); + if (pageNumber * pageSize < totalCount) { + pagingNext = "next"; + pageNumber++; + } + } + } while (pagingNext != null); + + // 保存为一条 CfgDaylightConfiguration,configName = "Property" + if (!allResults.isEmpty()) { + CfgDaylightConfiguration config = cfgDaylightConfigurationDao + .findByDlPropertyIdAndConfigName(null, "Property"); + List oldItems = config != null && StringUtils.isNotBlank(config.getConfigValue()) + ? JSONObject.parseArray(config.getConfigValue(), JSONObject.class) + : new ArrayList<>(); + if (config == null) { + config = new CfgDaylightConfiguration(); + config.setConfigName("Property"); + config.setDlPropertyId(null); + config.setCreatedAt(new Date()); + } + config.setConfigValue(JSONObject.toJSONString(allResults)); + config.setUpdatedAt(new Date()); + cfgDaylightConfigurationDao.save(config); + logConfigurationChanges("Property sync", null, "Property", oldItems, allResults); + } + + response.setSuccess(true); + response.setMessage("Successfully fetched and saved " + allResults.size() + " properties."); + + // 记录同步日志 + SysLogDaylightConfigurationSync syncLog = new SysLogDaylightConfigurationSync(); + syncLog.setConfigName("Property"); + syncLog.setLastSyncAt(syncStartTime); + syncLog.setSyncStatus("SUCCESS"); + syncLog.setRecordCount(allResults.size()); + syncLog.setCreatedAt(new Date()); + syncLogDao.save(syncLog); + } catch (Exception e) { + response.setSuccess(false); + response.setMessage(e.getMessage()); + + // 记录失败日志 + SysLogDaylightConfigurationSync syncLog = new SysLogDaylightConfigurationSync(); + syncLog.setConfigName("Property"); + syncLog.setLastSyncAt(syncStartTime); + syncLog.setSyncStatus("FAILED"); + syncLog.setRecordCount(0); + syncLog.setCreatedAt(new Date()); + syncLogDao.save(syncLog); + } + return response; + } + + private void logConfigurationChanges(String action, String dlPropertyId, String configType, + List oldItems, List newItems) { + Map oldItemMap = toConfigItemMap(oldItems); + List addedItems = new ArrayList<>(); + List updatedItems = new ArrayList<>(); + List removedItems = new ArrayList<>(); + + for (JSONObject newItem : newItems) { + String itemKey = getConfigItemKey(newItem); + JSONObject oldItem = oldItemMap.remove(itemKey); + if (oldItem == null) { + addedItems.add(formatConfigItem(newItem)); + } else if (!JSONObject.toJSONString(oldItem).equals(JSONObject.toJSONString(newItem))) { + updatedItems.add(formatConfigItem(newItem)); + } + } + + for (JSONObject removedItem : oldItemMap.values()) { + removedItems.add(formatConfigItem(removedItem)); + } + + log.info("{} configName={}, dlPropertyId={}, oldCount={}, newCount={}, addedCount={}, updatedCount={}, removedCount={}, added={}, updated={}, removed={}", + action, configType, dlPropertyId, oldItems.size(), newItems.size(), addedItems.size(), updatedItems.size(), + removedItems.size(), limitLogItems(addedItems), limitLogItems(updatedItems), limitLogItems(removedItems)); + } + + private void logConfigurationCreated(String action, String dlPropertyId, String configType, List newItems) { + log.info("{} configName={}, dlPropertyId={}, newCount={}, items={}", + action, configType, dlPropertyId, newItems.size(), limitLogItems(formatConfigItems(newItems))); + } + + private Map toConfigItemMap(List items) { + Map itemMap = new HashMap<>(); + for (int i = 0; i < items.size(); i++) { + JSONObject item = items.get(i); + itemMap.put(getConfigItemKey(item, i), item); + } + return itemMap; + } + + private List formatConfigItems(List items) { + List formattedItems = new ArrayList<>(); + for (JSONObject item : items) { + formattedItems.add(formatConfigItem(item)); + } + return formattedItems; + } + + private String getConfigItemKey(JSONObject item) { + return getConfigItemKey(item, -1); + } + + private String getConfigItemKey(JSONObject item, int index) { + String itemKey = firstNotBlank(item, "id", "code", "propertyId", "propertyCode", "name"); + if (StringUtils.isNotBlank(itemKey)) { + return itemKey; + } + return index >= 0 ? "index:" + index : JSONObject.toJSONString(item); + } + + private String formatConfigItem(JSONObject item) { + List parts = new ArrayList<>(); + addIfNotBlank(parts, "id", item.getString("id")); + addIfNotBlank(parts, "code", firstNotBlank(item, "code", "propertyCode")); + addIfNotBlank(parts, "name", firstNotBlank(item, "name", "displayName", "shortName")); + if (parts.isEmpty()) { + return JSONObject.toJSONString(item); + } + return StringUtils.join(parts, ","); + } + + private String firstNotBlank(JSONObject item, String... fieldNames) { + for (String fieldName : fieldNames) { + String value = item.getString(fieldName); + if (StringUtils.isNotBlank(value)) { + return value; + } + } + return null; + } + + private void addIfNotBlank(List parts, String name, String value) { + if (StringUtils.isNotBlank(value)) { + parts.add(name + "=" + value); + } + } + + private List limitLogItems(List items) { + int maxLogItemCount = 50; + if (items.size() <= maxLogItemCount) { + return items; + } + + List limitedItems = new ArrayList<>(items.subList(0, maxLogItemCount)); + limitedItems.add("... total " + items.size() + " items"); + return limitedItems; + } + + /** + * 从 Daylight API 拉取 block codes 列表,按 property 遍历,存入 cfg_daylight_configuration 表 + */ + public BaseApiResponse fetchDaylightBlockCodes() { + BaseApiResponse response = new BaseApiResponse(); + Date syncStartTime = new Date(); + + String dlPropertyId = null; + try { + TokenResponse daylightApiToken = daylightTokenService.getApiAccessToken(); + if (daylightApiToken == null || StringUtils.isBlank(daylightApiToken.getAccessToken())) { + response.setSuccess(false); + response.setMessage("Invoke daylight token failed"); + return response; + } + + List allProperties = propertyDao.findByCategory("DAYLIGHT"); + int savedCount = 0; + + if (allProperties == null || allProperties.isEmpty()) { + response.setSuccess(false); + response.setMessage("No properties found with category 'DAYLIGHT'"); + return response; + } + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); + + for (CfgProperties property : allProperties) { + dlPropertyId = property.getDlPropertyId(); + if (StringUtils.isBlank(dlPropertyId)) { + continue; + } + + // 查上次成功同步时间,决定是否走增量 + String updatedAtParam = ""; + Optional lastSync = syncLogDao + .findTopByConfigNameAndDlPropertyIdAndSyncStatusOrderByLastSyncAtDesc( + "BlockCode", dlPropertyId, "SUCCESS"); + CfgDaylightConfiguration existingConfig = cfgDaylightConfigurationDao + .findByDlPropertyIdAndConfigName(dlPropertyId, "BlockCode"); + if (lastSync.isPresent() && existingConfig != null) { + updatedAtParam = "&updatedAt=" + sdf.format(lastSync.get().getLastSyncAt()); + log.info("Property {} BlockCode incremental sync, updatedAt: {}", dlPropertyId, updatedAtParam); + } else { + if (lastSync.isPresent() && existingConfig == null) { + log.warn("Property {} BlockCode sync log found but config data missing, fallback to full sync", dlPropertyId); + } else { + log.info("Property {} BlockCode first sync, no updatedAt parameter", dlPropertyId); + } + } + + String path = "/api-gateway/data-api/v1/blocks"; + String apiUrl = daylightApiSetting.getServerUrl() + path; + List allResults = new ArrayList<>(); + + HttpHeaders headers = new HttpHeaders(); + headers.set("authorization", "Bearer " + daylightApiToken.getAccessToken()); + headers.set("Ac-Property-Id", dlPropertyId); + + String queryString = "pageSize=200" + updatedAtParam; + log.info("Fetching block codes for Ac-Property-Id: {}", dlPropertyId); + + ResponseEntity requestResponse = webProxyService.invokeRequest( + WebProxyService.DAYLIGHT_API, + HttpMethod.GET, + apiUrl, + queryString, + headers, + new byte[0], + Duration.ofSeconds(daylightApiSetting.getApiTimeout()), + httpErrorHandler + ).block(); + + if (requestResponse == null) { + response.setSuccess(false); + response.setMessage("Http request server can not visit."); + return response; + } + + response.setStatusCode(requestResponse.getStatusCode().value()); + + if (!requestResponse.getStatusCode().is2xxSuccessful()) { + byte[] errorBody = requestResponse.getBody(); + response.setSuccess(false); + response.setMessage(errorBody != null ? new String(errorBody, StandardCharsets.UTF_8) : "Request failed"); + return response; + } + + byte[] responseBody = requestResponse.getBody(); + if (responseBody != null) { + String jsonStr = new String(responseBody, StandardCharsets.UTF_8); + JSONObject responseJson = JSONObject.parseObject(jsonStr); + + List pageResults = responseJson.getJSONArray("results").toJavaList(JSONObject.class); + if (pageResults != null) { + allResults.addAll(pageResults); + } + } + + // 保存为一条 CfgDaylightConfiguration,configName = "BlockCode" + if (!allResults.isEmpty()) { + boolean isIncremental = StringUtils.isNotBlank(updatedAtParam); + if (isIncremental && existingConfig != null + && StringUtils.isNotBlank(existingConfig.getConfigValue())) { + // 增量同步:按 id 合并新旧数据 + List oldItems = JSONObject.parseArray(existingConfig.getConfigValue(), JSONObject.class); + Map mergedMap = new HashMap<>(); + for (JSONObject item : oldItems) { + mergedMap.put(getConfigItemKey(item), item); + } + logConfigurationChanges("BlockCode incremental sync", dlPropertyId, "BlockCode", oldItems, allResults); + for (JSONObject item : allResults) { + mergedMap.put(getConfigItemKey(item), item); + } + existingConfig.setConfigValue(JSONObject.toJSONString(new ArrayList<>(mergedMap.values()))); + existingConfig.setUpdatedAt(new Date()); + cfgDaylightConfigurationDao.save(existingConfig); + log.info("Merged BlockCode for {}: old={}, new={}, merged={}", + dlPropertyId, oldItems.size(), allResults.size(), mergedMap.size()); + } else { + // 全量同步:直接保存 + List oldItems = existingConfig != null + && StringUtils.isNotBlank(existingConfig.getConfigValue()) + ? JSONObject.parseArray(existingConfig.getConfigValue(), JSONObject.class) + : new ArrayList<>(); + CfgDaylightConfiguration config = existingConfig != null ? existingConfig : new CfgDaylightConfiguration(); + if (config.getConfigName() == null) { + config.setConfigName("BlockCode"); + config.setDlPropertyId(dlPropertyId); + } + if (config.getCreatedAt() == null) { + config.setCreatedAt(new Date()); + } + config.setConfigValue(JSONObject.toJSONString(allResults)); + config.setUpdatedAt(new Date()); + cfgDaylightConfigurationDao.save(config); + logConfigurationChanges("BlockCode full sync", dlPropertyId, "BlockCode", oldItems, allResults); + } + savedCount++; + } + } + + response.setSuccess(true); + response.setMessage("Successfully fetched and saved " + savedCount + " block code entries."); + + // 记录同步日志 + SysLogDaylightConfigurationSync syncLog = new SysLogDaylightConfigurationSync(); + syncLog.setConfigName("BlockCode"); + syncLog.setLastSyncAt(syncStartTime); + syncLog.setSyncStatus("SUCCESS"); + syncLog.setDlPropertyId(dlPropertyId); + syncLog.setRecordCount(savedCount); + syncLog.setCreatedAt(new Date()); + syncLogDao.save(syncLog); + } catch (Exception e) { + response.setSuccess(false); + response.setMessage(e.getMessage()); + + // 记录失败日志 + SysLogDaylightConfigurationSync syncLog = new SysLogDaylightConfigurationSync(); + syncLog.setConfigName("BlockCode"); + syncLog.setDlPropertyId(dlPropertyId); + syncLog.setLastSyncAt(syncStartTime); + syncLog.setSyncStatus("FAILED"); + syncLog.setRecordCount(0); + syncLog.setCreatedAt(new Date()); + syncLogDao.save(syncLog); + } + return response; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightTokenService.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightTokenService.java new file mode 100644 index 0000000..f754355 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightTokenService.java @@ -0,0 +1,227 @@ +package com.mgm.mcrm.apigateway.daylight.common.services; + +import com.alibaba.fastjson.JSON; +import com.mgm.mcrm.apigateway.common.dtos.HttpResult; +import com.mgm.mcrm.apigateway.common.dtos.config.DaylightApiSetting; +import com.mgm.mcrm.apigateway.daylight.common.dtos.CachedToken; +import com.mgm.mcrm.apigateway.daylight.common.dtos.TokenResponse; +import com.mgm.mcrm.apigateway.proxy.exception.HttpProxyErrorHandler; +import com.mgm.mcrm.apigateway.proxy.services.WebProxyService; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.LocalDateTime; + +@Service +public class DaylightTokenService { + private final Logger logger = LoggerFactory.getLogger(DaylightTokenService.class); + private final HttpProxyErrorHandler httpProxyErrorHandler = new HttpProxyErrorHandler(); + @Resource + private WebProxyService webProxyService; + @Autowired + private DaylightApiSetting daylightApiSetting; + private CachedToken cachedToken = null; + + private static final long RETRY_DELAY_MS = 1000; + private static final int MAX_PASSWORD_GRANT_ATTEMPTS = 3; + + public synchronized TokenResponse getApiAccessToken() throws Exception { + if (cachedToken != null && !isTokenExpire(cachedToken)) { + logger.info("Using cached Daylight access token because it has not expired."); + return cachedToken.getToken(); + } + + TokenResponse token = null; + TokenResponse expiredToken = cachedToken == null ? null : cachedToken.getToken(); + boolean passwordGrantRequired = true; + + // access token 过期后,优先使用缓存的 refresh token 获取新 token。 + if (expiredToken != null && StringUtils.isNotBlank(expiredToken.getRefreshToken())) { + logger.info("Daylight access token has expired; requesting a new token with refresh_token."); + token = requestToken(buildRefreshTokenRequestParams(expiredToken.getRefreshToken())); + if (token == null) { + logger.warn("Daylight refresh_token is expired or the refresh request failed; " + + "falling back to password grant."); + } else if (StringUtils.isBlank(token.getRefreshToken())) { + // 部分 OAuth 服务刷新成功后不会再次返回 refresh token,此时继续使用原 refresh token。 + token.setRefreshToken(expiredToken.getRefreshToken()); + } + if (token != null) { + passwordGrantRequired = false; + logger.info("Successfully obtained a new Daylight access token with refresh_token."); + } + } else if (expiredToken != null) { + logger.info("Daylight access token has expired and no refresh_token is available; " + + "requesting a new token with password grant."); + } else { + logger.info("No cached Daylight token is available; requesting a token with password grant."); + } + + // 首次请求、没有 refresh token 或刷新失败时,使用密码模式获取 token。 + if (passwordGrantRequired) { + token = requestTokenWithRetry(buildPasswordTokenRequestParams()); + if (token != null) { + logger.info("Successfully obtained a new Daylight access token with password grant."); + } + } + + if (token == null) { + logger.error("Failed to get a Daylight access token."); + return null; + } + + cacheToken(token); + return token; + } + + private TokenResponse requestTokenWithRetry(MultiValueMap params) + throws InterruptedException { + for (int attempt = 1; attempt <= MAX_PASSWORD_GRANT_ATTEMPTS; attempt++) { + HttpResult requestResult = invokeTokenRequest(params); + TokenResponse token = parseTokenResponse(requestResult); + if (token != null) { + return token; + } + + boolean shouldRetry = requestResult.getStatusCode() > 0 + && !isAuthenticationError(requestResult.getStatusCode()); + if (!shouldRetry || attempt == MAX_PASSWORD_GRANT_ATTEMPTS) { + break; + } + Thread.sleep(RETRY_DELAY_MS); + } + return null; + } + + private TokenResponse requestToken(MultiValueMap params) { + return parseTokenResponse(invokeTokenRequest(params)); + } + + private TokenResponse parseTokenResponse(HttpResult requestResult) { + if (requestResult.getStatusCode() < 200 || requestResult.getStatusCode() >= 300) { + return null; + } + try { + TokenResponse token = JSON.parseObject(requestResult.getResponseBody(), TokenResponse.class); + return token != null && StringUtils.isNotBlank(token.getAccessToken()) ? token : null; + } catch (Exception err) { + logger.error("Invalid Daylight token response:", err); + return null; + } + } + + private void cacheToken(TokenResponse token) { + if (cachedToken == null) { + cachedToken = new CachedToken(); + } + cachedToken.setToken(token); + + LocalDateTime currentTime = LocalDateTime.now(); + LocalDateTime expiryTime = currentTime.plusSeconds(daylightApiSetting.getTokenExpireSecond()); + try { + if (StringUtils.isNotBlank(token.getExpiresIn())) { + long tokenExpireVal = Math.max(0, Long.parseLong(token.getExpiresIn()) - 60); + expiryTime = currentTime.plusSeconds(tokenExpireVal); + } + } catch (NumberFormatException err) { + logger.warn("Invalid expires_in value in Daylight token response: {}", token.getExpiresIn()); + } + cachedToken.setExpireTime(expiryTime); + } + + private boolean isTokenExpire(CachedToken token) { + return token == null + || token.getExpireTime() == null + || !token.getExpireTime().isAfter(LocalDateTime.now()); + } + + private HttpHeaders buildHeaders() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + return headers; + } + + private MultiValueMap buildPasswordTokenRequestParams() { + MultiValueMap formData = new LinkedMultiValueMap<>(); + formData.add("username", daylightApiSetting.getUsername()); + formData.add("password", daylightApiSetting.getPassword()); + formData.add("grant_type", daylightApiSetting.getGrantType()); + formData.add("client_id", daylightApiSetting.getClientId()); + formData.add("client_secret", daylightApiSetting.getClientSecret()); + + if (StringUtils.isBlank(daylightApiSetting.getClientId())) { + logger.error("client_id is null or empty!"); + } + if (StringUtils.isBlank(daylightApiSetting.getClientSecret())) { + logger.error("client_secret is null or empty!"); + } + if (StringUtils.isBlank(daylightApiSetting.getGrantType())) { + logger.error("grant_type is null or empty!"); + } + return formData; + } + + private MultiValueMap buildRefreshTokenRequestParams(String refreshToken) { + MultiValueMap formData = new LinkedMultiValueMap<>(); + formData.add("grant_type", "refresh_token"); + formData.add("refresh_token", refreshToken); + formData.add("client_id", daylightApiSetting.getClientId()); + formData.add("client_secret", daylightApiSetting.getClientSecret()); + return formData; + } + + private boolean isAuthenticationError(int statusCode) { + return statusCode >= 400 && statusCode < 500; + } + + private HttpResult invokeTokenRequest(MultiValueMap params) { + HttpResult result = new HttpResult(); + String tokenUrl = daylightApiSetting.getServerUrl() + daylightApiSetting.getTokenUrl(); + try { + ResponseEntity requestRes = webProxyService.invokeRequest( + WebProxyService.DAYLIGHT_API, + HttpMethod.POST, + tokenUrl, + null, + buildHeaders(), + params, + Duration.ofSeconds(daylightApiSetting.getTokenRequestTimeout()), + httpProxyErrorHandler + ).block(); + if (requestRes != null) { + result.setStatusCode(requestRes.getStatusCode().value()); + result.setResponseHeaders(requestRes.getHeaders()); + byte[] resData = requestRes.getBody(); + if (resData != null) { + String bodyContent = new String(resData, StandardCharsets.UTF_8); + result.setResponseBody(bodyContent); + if (result.getStatusCode() < 200 || result.getStatusCode() >= 300) { + result.setErrorMessage(bodyContent); + } + } + } else { + result.setStatusCode(-1); + result.setErrorMessage("Http request server can not visit."); + } + } catch (Exception e) { + result.setStatusCode(-1); + result.setErrorMessage(e.getMessage()); + result.setExceptionClass(e.getClass().toString()); + logger.error("Failed to retrieve token from url: {}, Exception: {}", + tokenUrl, e.getMessage(), e); + } + return result; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/models/CfgDaylightConfiguration.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/models/CfgDaylightConfiguration.java new file mode 100644 index 0000000..1fa7ec2 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/models/CfgDaylightConfiguration.java @@ -0,0 +1,32 @@ +package com.mgm.mcrm.apigateway.daylight.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.util.Date; + +@Data +@Entity +@Table(name = "cfg_daylight_configuration") +public class CfgDaylightConfiguration { + + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "config_name") + private String configName; + + @Column(name = "config_value") + private String configValue; + + @Column(name = "dl_property_id") + private String dlPropertyId; + + @Column(name = "created_at") + private Date createdAt; + + @Column(name = "updated_at") + private Date updatedAt; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/models/SysLogDaylightConfigurationSync.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/models/SysLogDaylightConfigurationSync.java new file mode 100644 index 0000000..4de389a --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/models/SysLogDaylightConfigurationSync.java @@ -0,0 +1,35 @@ +package com.mgm.mcrm.apigateway.daylight.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.util.Date; + +@Data +@Entity +@Table(name = "sys_log_daylight_configuration_sync") +public class SysLogDaylightConfigurationSync { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + + @Column(name = "config_name") + private String configName; + + @Column(name = "last_sync_at") + private Date lastSyncAt; + + @Column(name = "sync_status") + private String syncStatus; + + @Column(name = "record_count") + private int recordCount; + + @Column(name = "dl_property_id") + private String dlPropertyId; + + @Column(name = "created_at") + private Date createdAt; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/CfgDaylightConfigurationDao.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/CfgDaylightConfigurationDao.java new file mode 100644 index 0000000..69c07ae --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/CfgDaylightConfigurationDao.java @@ -0,0 +1,32 @@ +package com.mgm.mcrm.apigateway.daylight.repositories; + +import com.mgm.mcrm.apigateway.daylight.models.CfgDaylightConfiguration; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +public interface CfgDaylightConfigurationDao extends JpaRepository { + + CfgDaylightConfiguration findByDlPropertyIdAndConfigName(String dlPropertyId, String configName); + + @Query("select case when count(c) > 0 then true else false end " + + "from CfgDaylightConfiguration c " + + "where c.configName = 'Property' " + + "and c.configValue is not null " + + "and c.configValue <> ''") + boolean existsSavedDaylightProperties(); + + @Query("select case when count(c) > 0 then true else false end " + + "from CfgDaylightConfiguration c " + + "where c.configName not in ('Property', 'BlockCode') " + + "and c.configValue is not null " + + "and c.configValue <> ''") + boolean existsSavedDaylightConfiguration(); + + @Query("select case when count(c) > 0 then true else false end " + + "from CfgDaylightConfiguration c " + + "where c.dlPropertyId = ?1 " + + "and c.configName not in ('Property', 'BlockCode') " + + "and c.configValue is not null " + + "and c.configValue <> ''") + boolean existsSavedConfigurationByDlPropertyId(String dlPropertyId); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/SysLogDaylightConfigurationSyncDao.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/SysLogDaylightConfigurationSyncDao.java new file mode 100644 index 0000000..4b95cb9 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/SysLogDaylightConfigurationSyncDao.java @@ -0,0 +1,14 @@ +package com.mgm.mcrm.apigateway.daylight.repositories; + +import com.mgm.mcrm.apigateway.daylight.models.SysLogDaylightConfigurationSync; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface SysLogDaylightConfigurationSyncDao extends JpaRepository { + + Optional findTopByConfigNameAndSyncStatusOrderByLastSyncAtDesc(String configName, String syncStatus); + + Optional findTopByConfigNameAndDlPropertyIdAndSyncStatusOrderByLastSyncAtDesc( + String configName, String dlPropertyId, String syncStatus); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/controllers/DaylightWebhook.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/controllers/DaylightWebhook.java new file mode 100644 index 0000000..6806c2b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/controllers/DaylightWebhook.java @@ -0,0 +1,65 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.controllers; + +import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; +import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.DaylightWebhookHandler; +import com.mgm.mcrm.apigateway.daylight.webhook.services.DaylightWebhookFactory; +import com.mgm.mcrm.apigateway.logging.services.WebhookLogService; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@Tag(name = "Allotment", description = "APIs for Room Allotment functions") +@RestController +@RequestMapping("/api/daylight/webhook") +public class DaylightWebhook { + @Resource + private WebhookLogService webhookLogService; + + @PostMapping(value = "/receive", produces = "application/json;charset=UTF-8") + public ResponseEntity handleRoomAvailableWebhook( + @RequestBody(required = true) String payload, + @RequestHeader Map headers, + HttpServletRequest request + ){ + BaseApiResponse result = new BaseApiResponse(); + String messageId = request.getHeader("Ac-Events-Message-Id"); + String eventType = request.getHeader("Ac-Events-Type"); + + if(StringUtils.isBlank(messageId)){ + result.setSuccess(false); + result.setMessage("Empty message id for this event"); + }else if(StringUtils.isBlank(eventType)){ + result.setSuccess(false); + result.setMessage("Empty event type for this event"); + }else if(StringUtils.isBlank(payload)){ + result.setSuccess(false); + result.setMessage("Empty payload for this event"); + }else{ + DaylightWebhookHandler handler = DaylightWebhookFactory.getHandler(eventType); + if(handler==null){ + result.setSuccess(false); + result.setMessage("No registered handler for event type["+eventType+"]"); + }else{ + boolean isDuplicated = webhookLogService.isMessageDuplicated(messageId); + if(isDuplicated){ + result.setSuccess(false); + result.setMessage("Message ["+messageId+"] already processed before"); + }else{ + try { + result = handler.handle(payload, headers); + }catch(Exception err){ + result.setSuccess(false); + result.setMessage("Handle Message ["+messageId+"] failed with error:"+err.getMessage()); + } + } + } + } + return ResponseEntity.status(HttpStatus.OK).body(result); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/BreakdownEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/BreakdownEntity.java new file mode 100644 index 0000000..f39db60 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/BreakdownEntity.java @@ -0,0 +1,18 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +import java.util.List; + +@Data +public class BreakdownEntity { + private String fromDate; + private String toDate; + private List guests; + private String marketSegmentId; + private RatePricingDetailsEntity ratePricingDetails; + private PriceEntity accommodationPrice; + private PriceEntity totalDailyPrice; + private List linkedPurchaseElements; + private DiscountEntity discount; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/CheckInDataEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/CheckInDataEntity.java new file mode 100644 index 0000000..77b4a98 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/CheckInDataEntity.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class CheckInDataEntity { + private String userId; + private String utcTimestamp; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ContactEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ContactEntity.java new file mode 100644 index 0000000..d6e65b7 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ContactEntity.java @@ -0,0 +1,11 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +import java.util.List; + +@Data +public class ContactEntity { + private String id; + private List contactProfileRole; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ContactProfileEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ContactProfileEntity.java new file mode 100644 index 0000000..c839fd4 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ContactProfileEntity.java @@ -0,0 +1,11 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +import java.util.List; + +@Data +public class ContactProfileEntity { + private ContactEntity primaryContact; + private List otherContacts; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/DiscountEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/DiscountEntity.java new file mode 100644 index 0000000..3a40aba --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/DiscountEntity.java @@ -0,0 +1,10 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class DiscountEntity { + private double amount; + private ReasonEntity reason; + private String type; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/EmbeddedEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/EmbeddedEntity.java new file mode 100644 index 0000000..68f0119 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/EmbeddedEntity.java @@ -0,0 +1,13 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +import java.util.List; + +@Data +public class EmbeddedEntity { + private List breakdown; + private List purchaseElements; + private Object policies; + private List guestMessages; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ExternalSystemIdEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ExternalSystemIdEntity.java new file mode 100644 index 0000000..8e1abe0 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ExternalSystemIdEntity.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class ExternalSystemIdEntity { + private String id; + private String value; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/GuaranteedTimeEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/GuaranteedTimeEntity.java new file mode 100644 index 0000000..8bc0d5d --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/GuaranteedTimeEntity.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class GuaranteedTimeEntity { + private String guaranteedTimeOfArrival; + private String guaranteedTimeOfDeparture; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/GuestEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/GuestEntity.java new file mode 100644 index 0000000..4f8ff2d --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/GuestEntity.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class GuestEntity { + private String ageBucketId; + private int count; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/GuestMessageEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/GuestMessageEntity.java new file mode 100644 index 0000000..91205ed --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/GuestMessageEntity.java @@ -0,0 +1,7 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class GuestMessageEntity { +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/LinkedCompanyEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/LinkedCompanyEntity.java new file mode 100644 index 0000000..519b58a --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/LinkedCompanyEntity.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class LinkedCompanyEntity { + private String id; + private ContactProfileEntity contactProfiles; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/PolicyEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/PolicyEntity.java new file mode 100644 index 0000000..00b30d1 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/PolicyEntity.java @@ -0,0 +1,7 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class PolicyEntity { +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/PreferenceEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/PreferenceEntity.java new file mode 100644 index 0000000..ec0c387 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/PreferenceEntity.java @@ -0,0 +1,10 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class PreferenceEntity { + private String id; + private boolean isRequired; + private int weight; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/PriceEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/PriceEntity.java new file mode 100644 index 0000000..4e3348a --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/PriceEntity.java @@ -0,0 +1,11 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class PriceEntity { + private RevenueAmountEntity amount; + private String currency; + private String basePriceType; + private boolean isConfidential; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ProjectedRevenueEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ProjectedRevenueEntity.java new file mode 100644 index 0000000..23eecd4 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ProjectedRevenueEntity.java @@ -0,0 +1,13 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class ProjectedRevenueEntity { + private String amount; + private String currency; + private boolean isConfidential; + private double gross; + private double net; + private String basePriceType; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/PurchaseElementEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/PurchaseElementEntity.java new file mode 100644 index 0000000..cbc5623 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/PurchaseElementEntity.java @@ -0,0 +1,7 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class PurchaseElementEntity { +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/RatePricingDetailsEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/RatePricingDetailsEntity.java new file mode 100644 index 0000000..3fa5716 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/RatePricingDetailsEntity.java @@ -0,0 +1,11 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class RatePricingDetailsEntity { + private String ratePlanId; + private String roomTypeToChargeId; + private boolean isConfidential; + private boolean isEmergencyOffer; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ReasonEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ReasonEntity.java new file mode 100644 index 0000000..55305be --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ReasonEntity.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class ReasonEntity { + private String id; + private String comment; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ReservationNotificationEvent.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ReservationNotificationEvent.java new file mode 100644 index 0000000..1312f2d --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ReservationNotificationEvent.java @@ -0,0 +1,60 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +import java.util.List; + +@Data +public class ReservationNotificationEvent { + private String trigger; + private String propertyId; + private String reservationId; + private String correlationId; + private String profileId; + private StatusCodeEntity statusCode; + private String confirmationNumber; + private String eta; + private String etd; + private String checkInTime; + private String checkOutTime; + private String arrivalDate; + private String departureDate; + private GuaranteedTimeEntity guaranteedTimes; + private String roomTypeId; + private String roomId; + private String guaranteeTypeId; + private String channelId; + private List preferences; + private String sourceId; + private List accompanyingGuestProfileIds; + private List applicableMembershipIds; + private boolean isRoomFixed; + private String cancellationComment; + private String cancellingUserId; + private String cancellationTime; + private String cancellationReasonId; + private String cancellationNumber; + private String sharingId; + private String groupId; + private ProjectedRevenueEntity projectedRevenue; + private List externalSystemIds; + private TransportationsEntity transportations; + private String bookingDate; + private String accountId; + private LinkedCompanyEntity linkedCompany; + private LinkedCompanyEntity linkedTravelAgent; + private ContactProfileEntity guestContactProfiles; + private int version; + private String purposeOfStayId; + private String updatedAt; + private String createdAt; + private ProjectedRevenueEntity projectedRevenueAmount; + private String bookingTimestamp; + private RoomDetailEntity previousRoomDetails; + private WalkInDataEntity walkInData; + private boolean emergencyOffer; + private String blockId; + private EmbeddedEntity _embedded; + + private String dlExternalNo; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/RevenueAmountEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/RevenueAmountEntity.java new file mode 100644 index 0000000..28e5b31 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/RevenueAmountEntity.java @@ -0,0 +1,12 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class RevenueAmountEntity { + private boolean isConfidential; + private double gross; + private double net; + private String currency; + private String basePriceType; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/RoomDetailEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/RoomDetailEntity.java new file mode 100644 index 0000000..c9d395f --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/RoomDetailEntity.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class RoomDetailEntity { + private String roomTypeId; + private String roomId; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/StatusCodeEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/StatusCodeEntity.java new file mode 100644 index 0000000..9c2d9e5 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/StatusCodeEntity.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class StatusCodeEntity { + private String code; + private String description; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/TransportationEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/TransportationEntity.java new file mode 100644 index 0000000..2028556 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/TransportationEntity.java @@ -0,0 +1,7 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class TransportationEntity { +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/TransportationsEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/TransportationsEntity.java new file mode 100644 index 0000000..f77fd2f --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/TransportationsEntity.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class TransportationsEntity { + private TransportationEntity arrivalTransportation; + private TransportationEntity departureTransportation; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/WalkInDataEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/WalkInDataEntity.java new file mode 100644 index 0000000..3b4f493 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/WalkInDataEntity.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class WalkInDataEntity { + private CheckInDataEntity checkInData; + private boolean walkInIntention; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/common/AllocationsVariantsEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/common/AllocationsVariantsEntity.java new file mode 100644 index 0000000..a5726ac --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/common/AllocationsVariantsEntity.java @@ -0,0 +1,8 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.common; + +import lombok.Data; + +@Data +public class AllocationsVariantsEntity { + private int additionalProperties; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/common/ChangeAvailabilityEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/common/ChangeAvailabilityEntity.java new file mode 100644 index 0000000..15a3e94 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/common/ChangeAvailabilityEntity.java @@ -0,0 +1,15 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.common; + +import lombok.Data; + +@Data +public class ChangeAvailabilityEntity { + private int available; + private int availableWithOverbooking; + private int allDeduct; + private int allNonDeduct; + private int blocksDeduct; + private int blocksNonDeduct; + private int outOfService; + private int outOfOrder; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/common/RoomAvailabilityEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/common/RoomAvailabilityEntity.java new file mode 100644 index 0000000..7ca4b3b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/common/RoomAvailabilityEntity.java @@ -0,0 +1,14 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.common; + +import lombok.Data; + +@Data +public class RoomAvailabilityEntity { + private int definitelyAvailableRooms; + private int tentativelySoldRooms; + private int ooo; + private int oos; + private int definitelySoldRooms; + private int availableRoomsWithOverbooking; + private int physicalRooms; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/common/RoomCounterEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/common/RoomCounterEntity.java new file mode 100644 index 0000000..046fbfd --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/common/RoomCounterEntity.java @@ -0,0 +1,13 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.common; + +import lombok.Data; + +@Data +public class RoomCounterEntity { + private int totalRooms; + private int sellLimit; + private int overbookingLimit; + private int allDeduct; + private int allNonDeduct; + private AllocationsVariantsEntity allocationsVariants; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomcount/AvailableRoomCountUpdatedEvent.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomcount/AvailableRoomCountUpdatedEvent.java new file mode 100644 index 0000000..4b071aa --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomcount/AvailableRoomCountUpdatedEvent.java @@ -0,0 +1,11 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomcount; + +import lombok.Data; + +import java.util.List; + +@Data +public class AvailableRoomCountUpdatedEvent { + private PropertyAvailabilityEntity propertyAvailability; + private List roomTypesAvailabilities; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomcount/DateAvailabilityEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomcount/DateAvailabilityEntity.java new file mode 100644 index 0000000..5fc1a2e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomcount/DateAvailabilityEntity.java @@ -0,0 +1,14 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomcount; + + +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.common.RoomAvailabilityEntity; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.common.RoomCounterEntity; +import lombok.Data; + +@Data +public class DateAvailabilityEntity { + private String from; + private String to; + private RoomAvailabilityEntity availability; + private RoomCounterEntity counters; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomcount/PropertyAvailabilityEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomcount/PropertyAvailabilityEntity.java new file mode 100644 index 0000000..9b79c9b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomcount/PropertyAvailabilityEntity.java @@ -0,0 +1,10 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomcount; + +import lombok.Data; + +import java.util.List; + +@Data +public class PropertyAvailabilityEntity { + private List dateAvailabilities; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomcount/RoomTypeAvailabilityEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomcount/RoomTypeAvailabilityEntity.java new file mode 100644 index 0000000..78464e7 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomcount/RoomTypeAvailabilityEntity.java @@ -0,0 +1,12 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomcount; + +import lombok.Data; + +import java.util.List; + +@Data +public class RoomTypeAvailabilityEntity { + private String roomTypeId; + private String blockId; + private List dateAvailabilities; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomtype/AvailabilityEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomtype/AvailabilityEntity.java new file mode 100644 index 0000000..d65e366 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomtype/AvailabilityEntity.java @@ -0,0 +1,13 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomtype; + +import lombok.Data; + +import java.util.List; + +@Data +public class AvailabilityEntity { + private String roomTypeId; + private String blockId; + private List dateAvailabilityChanges; + private String to; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomtype/DateAvailabilityChangeEntity.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomtype/DateAvailabilityChangeEntity.java new file mode 100644 index 0000000..9e70289 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomtype/DateAvailabilityChangeEntity.java @@ -0,0 +1,13 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomtype; + +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.common.ChangeAvailabilityEntity; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.common.RoomCounterEntity; +import lombok.Data; + +@Data +public class DateAvailabilityChangeEntity { + private ChangeAvailabilityEntity availability; + private RoomCounterEntity counters; + private String from; + private String blockId; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomtype/RoomTypeDateAvailabilityChangedEvent.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomtype/RoomTypeDateAvailabilityChangedEvent.java new file mode 100644 index 0000000..4ffc252 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/room/roomtype/RoomTypeDateAvailabilityChangedEvent.java @@ -0,0 +1,13 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomtype; + +import lombok.Data; + +import java.util.List; + +@Data +public class RoomTypeDateAvailabilityChangedEvent { + private String groupId; + private List availabilities; + private String inventoryId; + private String blockId; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/DaylightWebhookHandler.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/DaylightWebhookHandler.java new file mode 100644 index 0000000..3212682 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/DaylightWebhookHandler.java @@ -0,0 +1,11 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers; + + + +import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; + +import java.util.Map; + +public interface DaylightWebhookHandler { + BaseApiResponse handle(String payload, Map headers) throws Exception; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/AvailableRoomCountUpdatedHandler.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/AvailableRoomCountUpdatedHandler.java new file mode 100644 index 0000000..017f367 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/AvailableRoomCountUpdatedHandler.java @@ -0,0 +1,69 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.mgm.mcrm.apigateway.allotment.services.RoomCriteriaDaylightService; +import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.common.RoomCounterEntity; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomcount.AvailableRoomCountUpdatedEvent; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomcount.DateAvailabilityEntity; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomcount.RoomTypeAvailabilityEntity; +import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.DaylightWebhookHandler; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class AvailableRoomCountUpdatedHandler implements DaylightWebhookHandler { + private final RoomCriteriaDaylightService roomCriteriaDaylightService; + @Override + public BaseApiResponse handle(String payload, Map headers) { + BaseApiResponse result = new BaseApiResponse(); + AvailableRoomCountUpdatedEvent eventData = null; + try{ + eventData = JSONObject.parseObject(payload, AvailableRoomCountUpdatedEvent.class); + }catch(Exception err){ + result.setSuccess(false); + result.setMessage("Convert Payload failed:["+err.getMessage()+"]"); + } + if(eventData!=null){ + String propertyId = headers.get("ac-property-id"); + if(StringUtils.isNotBlank(propertyId)){ + List roomTypeAvailabilities = eventData.getRoomTypesAvailabilities(); + if(roomTypeAvailabilities!= null && !roomTypeAvailabilities.isEmpty()){ + for(RoomTypeAvailabilityEntity roomItem:roomTypeAvailabilities){ + String roomTypeId = roomItem.getRoomTypeId(); + String blockCodeId = roomItem.getBlockId(); + List dateAvailabilities = roomItem.getDateAvailabilities(); + if(dateAvailabilities!=null && !dateAvailabilities.isEmpty()){ + for(DateAvailabilityEntity dateItem : dateAvailabilities){ + RoomCounterEntity counter = dateItem.getCounters(); + if(counter!=null){ + String fromDateValue = dateItem.getFrom(); + String toDateValue = dateItem.getTo(); + roomCriteriaDaylightService.processDaylightRoomTypeAllotmentUpdate( + "AvailableRoomCountUpdated", + propertyId, + roomTypeId, + blockCodeId, + fromDateValue, + toDateValue, + counter); + } + } + } + } + } + result.setSuccess(true); + }else{ + result.setSuccess(false); + result.setMessage("Daylight Property Id is empty in event"); + } + + } + return result; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationNotificationHandler.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationNotificationHandler.java new file mode 100644 index 0000000..8ac1f20 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationNotificationHandler.java @@ -0,0 +1,117 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; +import com.mgm.mcrm.apigateway.common.enums.EumnDaylightStatus; +import com.mgm.mcrm.apigateway.common.utils.DateTimeUtil; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation.ReservationNotificationEvent; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation.StatusCodeEntity; +import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.DaylightWebhookHandler; +import com.mgm.mcrm.apigateway.reservation.models.Bookings; +import com.mgm.mcrm.apigateway.reservation.services.BookingService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; + +@Slf4j +@Component +@RequiredArgsConstructor +public class ReservationNotificationHandler implements DaylightWebhookHandler { + + private final BookingService bookingService; + + + @Override + public BaseApiResponse handle(String payload, Map headers) throws Exception { + BaseApiResponse result = new BaseApiResponse(); + ReservationNotificationEvent eventData = null; + try{ + eventData = JSONObject.parseObject(payload, ReservationNotificationEvent.class); + }catch(Exception err){ + result.setSuccess(false); + result.setMessage("Convert Payload failed:["+err.getMessage()+"]"); + } + if(eventData!=null){ + + Bookings daylightBooking = convertEventToBooking(eventData); + bookingService.processDaylightUpdateBooking(daylightBooking); + result.setSuccess(true); + } + return result; + } + + private Bookings convertEventToBooking(ReservationNotificationEvent eventData) { + + log.info("Reservation Notification Event Data: {}", JSON.toJSONString(eventData)); + + Bookings result = new Bookings(); + LocalDateTime checkoutDate = DateTimeUtil.getDateFromString(eventData.getDepartureDate(), "yyyy-MM-dd"); + result.setConfirmationNo(eventData.getConfirmationNumber()); + result.setDlReservationId(eventData.getReservationId()); + result.setDlProfileId(eventData.getProfileId()); + result.setDlGuaranteeTypeId(eventData.getGuaranteeTypeId()); + result.setDlSourceId(eventData.getSourceId()); + result.setDlChannelId(eventData.getChannelId()); + result.setDlAccountId(eventData.getAccountId()); + result.setDlBlockId(eventData.getBlockId()); + result.setDlExternalNo(eventData.getDlExternalNo()); + if (eventData.getExternalSystemIds() != null + && !eventData.getExternalSystemIds().isEmpty()) { + result.setDlExternalNo(eventData.getExternalSystemIds().getFirst().getValue()); + } + result.setCreatedAt(parseDateTime(eventData.getCreatedAt())); + result.setUpdatedAt(parseDateTime(eventData.getUpdatedAt())); + result.setCheckinDate(DateTimeUtil.getDateFromString(eventData.getArrivalDate(),"yyyy-MM-dd")); + result.setCheckoutDate(checkoutDate); + result.setCategory("DAYLIGHT"); + + String dlMarketCodeId = eventData.get_embedded().getBreakdown().getFirst().getMarketSegmentId(); + String dlRatePlanId = eventData.get_embedded().getBreakdown().getFirst().getRatePricingDetails().getRatePlanId(); + + result.setDlMarketSegmentId(dlMarketCodeId); + result.setDlRatePlanId(dlRatePlanId); + + log.info("Webhook: Reservation confirmationNumber={}, reservationId={}, profileId={}", + eventData.getConfirmationNumber(), eventData.getReservationId(), eventData.getProfileId()); + Bookings oneByDlProfileId = bookingService.findOneByDlProfileId(eventData.getProfileId()); + if (oneByDlProfileId != null) { + result.setPlayerId(oneByDlProfileId.getPlayerId()); + log.info("PlayerId={}", oneByDlProfileId.getPlayerId()); + } else { + log.info("Player id not found"); + // TODO 去pms查 profile, 如果这个 profile 没有membership,那么不保存进 db by Mauve 20260731 + } + + StatusCodeEntity statusCode = eventData.getStatusCode(); + if(statusCode!=null) { + result.setLastOperaStatus(EumnDaylightStatus.getStatusByDaylightStatus(statusCode.getDescription().toUpperCase())); + } + result.setDlPropertyId(eventData.getPropertyId()); + result.setDlRoomTypeId(eventData.getRoomTypeId()); + String eta = eventData.getEta(); + if(StringUtils.isNotBlank(eta)) { + LocalDateTime dateTime = LocalDateTime.parse(eta); + String etaTime = dateTime.format(DateTimeFormatter.ofPattern("HH:mm")); + result.setEta(etaTime); + } + return result; + } + + private LocalDateTime parseDateTime(String value) { + if (StringUtils.isBlank(value)) { + return null; + } + try { + return LocalDateTime.parse(value); + } catch (Exception err) { + log.warn("Unable to parse reservation timestamp: {}", value); + return null; + } + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/RoomTypeDateAvailabilityChangedHandler.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/RoomTypeDateAvailabilityChangedHandler.java new file mode 100644 index 0000000..ef19c38 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/RoomTypeDateAvailabilityChangedHandler.java @@ -0,0 +1,70 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl; + +import com.alibaba.fastjson.JSONObject; +import com.mgm.mcrm.apigateway.allotment.services.RoomCriteriaDaylightService; +import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.common.RoomCounterEntity; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomtype.AvailabilityEntity; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomtype.DateAvailabilityChangeEntity; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.room.roomtype.RoomTypeDateAvailabilityChangedEvent; +import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.DaylightWebhookHandler; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +@Component +@RequiredArgsConstructor +public class RoomTypeDateAvailabilityChangedHandler implements DaylightWebhookHandler { + private final RoomCriteriaDaylightService roomCriteriaDaylightService; + + @Override + public BaseApiResponse handle(String payload, Map headers) { + BaseApiResponse result = new BaseApiResponse(); + RoomTypeDateAvailabilityChangedEvent eventData = null; + try{ + eventData = JSONObject.parseObject(payload, RoomTypeDateAvailabilityChangedEvent.class); + }catch(Exception err){ + result.setSuccess(false); + result.setMessage("Convert Payload failed:["+err.getMessage()+"]"); + } + if(eventData!=null){ + String propertyId = headers.get("ac-property-id"); + if(StringUtils.isNotBlank(propertyId)){ + List roomTypeAvailabilities = eventData.getAvailabilities(); + if(roomTypeAvailabilities!= null && !roomTypeAvailabilities.isEmpty()){ + for(AvailabilityEntity roomItem:roomTypeAvailabilities){ + String roomTypeId = roomItem.getRoomTypeId(); + String dlBlockCodeId = roomItem.getBlockId(); + String toDateValue = roomItem.getTo(); + List dateAvailabilities = roomItem.getDateAvailabilityChanges(); + if(dateAvailabilities!=null && !dateAvailabilities.isEmpty()){ + for(DateAvailabilityChangeEntity changeItem : dateAvailabilities){ + RoomCounterEntity counter =changeItem.getCounters(); + if(counter!=null){ + String fromDateValue = changeItem.getFrom(); + roomCriteriaDaylightService.processDaylightRoomTypeAllotmentUpdate( + "RoomTypeDateAvailabilityChanged", + propertyId, + roomTypeId, + dlBlockCodeId, + fromDateValue, + toDateValue, + counter); + } + } + } + } + } + result.setSuccess(true); + }else{ + result.setSuccess(false); + result.setMessage("Daylight Property Id is empty in event"); + } + + } + return result; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/services/DaylightWebhookFactory.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/services/DaylightWebhookFactory.java new file mode 100644 index 0000000..215faad --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/services/DaylightWebhookFactory.java @@ -0,0 +1,19 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.services; + + +import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.DaylightWebhookHandler; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class DaylightWebhookFactory { + private static final Map handlers = new ConcurrentHashMap<>(); + + public static DaylightWebhookHandler getHandler(String eventType) { + return handlers.get(eventType); + } + + public static void registerHandler(String eventType, DaylightWebhookHandler handler) { + handlers.put(eventType, handler); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/EsbToken.java b/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/EsbToken.java new file mode 100644 index 0000000..15fa9a0 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/EsbToken.java @@ -0,0 +1,17 @@ +package com.mgm.mcrm.apigateway.esb.common.dtos; + +import com.alibaba.fastjson2.annotation.JSONField; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class EsbToken { + @JSONField(name = "StatusCode") + private String statusCode; + @JSONField(name = "Token") + private String accessToken; + @JSONField(name = "ExpireTime") + private String expireTimeVal; + private LocalDateTime expireTime; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/PlayerProfileApiResult.java b/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/PlayerProfileApiResult.java new file mode 100644 index 0000000..4d0d069 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/PlayerProfileApiResult.java @@ -0,0 +1,188 @@ +package com.mgm.mcrm.apigateway.esb.common.dtos.mgmapi; + +import java.util.ArrayList; +import java.util.List; + + +public class PlayerProfileApiResult { + + private String statusCode = null; + private String statusDescription=""; + private String playerId; + private String userId; + private String tierName; + private String tierId; + private String chineseName; + private String englishName; + private String salutation; + private Integer codedFlag; + private String name; + private String playerPhone = null; + private String phoneType = null; + private String birthday = null; + private String country = null; + private String state; + private String passport = null; + private String language = null; + private List nameIds=new ArrayList(); + + public PlayerProfileApiResult() { + + } + + public String getStatusCode() { + return statusCode; + } + + public void setStatusCode(String statusCode) { + this.statusCode = statusCode; + } + + public String getStatusDescription() { + return statusDescription; + } + + public void setStatusDescription(String statusDescription) { + this.statusDescription = statusDescription; + } + + public String getPlayerId() { + return playerId; + } + + public void setPlayerId(String playerId) { + this.playerId = playerId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getTierName() { + return tierName; + } + + public void setTierName(String tierName) { + this.tierName = tierName; + } + + public String getTierId() { + return tierId; + } + + public void setTierId(String tierId) { + this.tierId = tierId; + } + + public String getChineseName() { + return chineseName; + } + + public void setChineseName(String chineseName) { + this.chineseName = chineseName; + } + + public String getEnglishName() { + return englishName; + } + + public void setEnglishName(String englishName) { + this.englishName = englishName; + } + + public String getSalutation() { + return salutation; + } + + public void setSalutation(String salutation) { + this.salutation = salutation; + } + + public List getNameIds() { + return nameIds; + } + + public void setNameIds(List nameIds) { + this.nameIds = nameIds; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getCodedFlag() { + return codedFlag; + } + + public void setCodedFlag(Integer codedFlag) { + this.codedFlag = codedFlag; + } + + public String getPlayerPhone() { + return playerPhone; + } + + public void setPlayerPhone(String playerPhone) { + this.playerPhone = playerPhone; + } + + public String getPhoneType() { + return phoneType; + } + + public void setPhoneType(String phoneType) { + this.phoneType = phoneType; + } + + public String getBirthday() { + return birthday; + } + + public void setBirthday(String birthday) { + this.birthday = birthday; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getPassport() { + return passport; + } + + public void setPassport(String passport) { + this.passport = passport; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/UserInfoApiRecord.java b/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/UserInfoApiRecord.java new file mode 100644 index 0000000..7c2226e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/UserInfoApiRecord.java @@ -0,0 +1,226 @@ +package com.mgm.mcrm.apigateway.esb.common.dtos.mgmapi; + + +public class UserInfoApiRecord { + + private String statusCode = null; + private String statusDescription=""; + private String userId; + private String userName; + private String property; + private String division; + private String department; + private String section; + private String block; + private Long propertyId; + private Long divisionId; + private Long departmentId; + private Long sectionId; + private Long blockId; + private Long teamStructureId; + private Boolean canViewBooking=false; + private Boolean canNewBooking=false; + private Boolean canViewTodoList=false; + private Boolean canViewDashboard=false; + private String groupName; + private String phone; + private boolean loginTag; + + public UserInfoApiRecord() { + + } + + + public String getStatusCode() { + return statusCode; + } + + public void setStatusCode(String statusCode) { + this.statusCode = statusCode; + } + + public String getStatusDescription() { + return statusDescription; + } + + public void setStatusDescription(String statusDescription) { + this.statusDescription = statusDescription; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getProperty() { + return property; + } + + public void setProperty(String property) { + this.property = property; + } + + public String getDivision() { + return division; + } + + public void setDivision(String division) { + this.division = division; + } + + public String getDepartment() { + return department; + } + + public void setDepartment(String department) { + this.department = department; + } + + public String getSection() { + return section; + } + + public void setSection(String section) { + this.section = section; + } + + public String getBlock() { + return block; + } + + public void setBlock(String block) { + this.block = block; + } + + public Long getPropertyId() { + return propertyId; + } + + public void setPropertyId(Long propertyId) { + this.propertyId = propertyId; + } + + public Long getDivisionId() { + return divisionId; + } + + public void setDivisionId(Long divisionId) { + this.divisionId = divisionId; + } + + public Long getDepartmentId() { + return departmentId; + } + + public void setDepartmentId(Long departmentId) { + this.departmentId = departmentId; + } + + public Long getSectionId() { + return sectionId; + } + + public void setSectionId(Long sectionId) { + this.sectionId = sectionId; + } + + public Long getBlockId() { + return blockId; + } + + public void setBlockId(Long blockId) { + this.blockId = blockId; + } + + public Long getTeamStructureId() { + return teamStructureId; + } + + public void setTeamStructureId(Long teamStructureId) { + this.teamStructureId = teamStructureId; + } + + public boolean isLoginTag() { + return loginTag; + } + + public void setLoginTag(boolean loginTag) { + this.loginTag = loginTag; + } + + + public Boolean getCanViewBooking() { + return canViewBooking; + } + + + public void setCanViewBooking(Boolean canViewBooking) { + this.canViewBooking = canViewBooking; + } + + + public Boolean getCanNewBooking() { + return canNewBooking; + } + + + public void setCanNewBooking(Boolean canNewBooking) { + this.canNewBooking = canNewBooking; + } + + + + + public Boolean getCanViewTodoList() { + return canViewTodoList; + } + + + public void setCanViewTodoList(Boolean canViewTodoList) { + this.canViewTodoList = canViewTodoList; + } + + + public Boolean getCanViewDashboard() { + return canViewDashboard; + } + + + public void setCanViewDashboard(Boolean canViewDashboard) { + this.canViewDashboard = canViewDashboard; + } + + + public String getGroupName() { + return groupName; + } + + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + + public String getPhone() { + return phone; + } + + + public void setPhone(String phone) { + this.phone = phone; + } + + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/UserInfoApiResult.java b/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/UserInfoApiResult.java new file mode 100644 index 0000000..e66f452 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/esb/common/dtos/mgmapi/UserInfoApiResult.java @@ -0,0 +1,31 @@ +package com.mgm.mcrm.apigateway.esb.common.dtos.mgmapi; + + +import lombok.Data; + +import java.util.List; + +@Data +public class UserInfoApiResult { + + private String statusCode = null; + private String statusDescription=""; + private String userId; + private String userName; + private String property; + private String division; + private String department; + private String section; + private String block; + private Long propertyId; + private Long divisionId; + private Long departmentId; + private Long sectionId; + private Long blockId; + private Long teamStructureId; + private Boolean canViewBooking=false; + private Boolean canNewBooking=false; + private String groupName; + private boolean loginTag; + private List records; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/EsbTokenService.java b/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/EsbTokenService.java new file mode 100644 index 0000000..2a0f62b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/EsbTokenService.java @@ -0,0 +1,158 @@ +package com.mgm.mcrm.apigateway.esb.common.services; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.mgm.mcrm.apigateway.common.dtos.HttpResult; +import com.mgm.mcrm.apigateway.common.dtos.config.EsbApiSetting; +import com.mgm.mcrm.apigateway.common.utils.DateTimeUtil; +import com.mgm.mcrm.apigateway.esb.common.dtos.EsbToken; +import com.mgm.mcrm.apigateway.proxy.exception.HttpProxyErrorHandler; +import com.mgm.mcrm.apigateway.proxy.services.WebProxyService; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.time.LocalDateTime; + + +@Service +public class EsbTokenService { + private final Logger logger = LoggerFactory.getLogger(EsbTokenService.class); + private final HttpProxyErrorHandler httpProxyErrorHandler=new HttpProxyErrorHandler(); + @Resource + private WebProxyService webProxyService; + @Autowired + private EsbApiSetting esbApiSetting; + private EsbToken cachedToken = new EsbToken(); + + // 重试间隔时间:1秒 + private final long RETRY_DELAY_MS = 1000; + + public EsbToken getApiAccessToken() throws Exception { + EsbToken returnToken = null; + int retryCnt=0; + boolean isNeedTry=true; + String lastErrorMsg =""; + if (!isTokenExpire(cachedToken)) { + return cachedToken; + }else{ + while(returnToken==null && isNeedTry && retryCnt<3){ + HttpResult requestResult = this.invokeTokenRequest(); + if(requestResult.getStatusCode()>=200 && requestResult.getStatusCode()<300){ + try{ + EsbToken newToken = JSON.parseObject(requestResult.getResponseBody(), EsbToken.class); + if("OK".equalsIgnoreCase(newToken.getStatusCode()) + && StringUtils.isNotBlank(newToken.getAccessToken()) + ){ + LocalDateTime expiryTime =DateTimeUtil.getDateFromString(newToken.getExpireTimeVal(), "yyyy-MM-dd HH:mm:ss"); + if(expiryTime!=null){ + newToken.setExpireTime(expiryTime.minusSeconds(60)); + }else{ + expiryTime = LocalDateTime.now().plusSeconds(esbApiSetting.getTokenExpireSecond()); + newToken.setExpireTime(expiryTime); + } + returnToken = newToken; + } + }catch(Exception err){ + logger.error("Invalid token response after cache expired:", err); + } + }else{ + lastErrorMsg = requestResult.getErrorMessage(); + } + isNeedTry = (returnToken==null && requestResult.getStatusCode()>0 && !(isAuthenticationError(requestResult.getStatusCode()))); + if(isNeedTry) { + retryCnt++; + Thread.sleep(RETRY_DELAY_MS); + } + } + } + if(returnToken==null){ + logger.error("Failed to get new token after cache expired with error: {}", lastErrorMsg); + }else{ + cachedToken = returnToken; + } + return returnToken; + } + + private boolean isTokenExpire(EsbToken token){ + return token==null + || StringUtils.isBlank(token.getAccessToken()) + || token.getExpireTime()==null + || token.getExpireTime().isBefore(LocalDateTime.now()); + } + + /** + * 构建请求头 + */ + private HttpHeaders buildHeaders() { + HttpHeaders headers = new HttpHeaders(); + headers.add("Content-type", "application/json;charset=utf-8"); + return headers; + } + + private byte[] buildTokenRequestBody() { + JSONObject requestObj = new JSONObject(); + requestObj.put("AppId", esbApiSetting.getAppId()); + requestObj.put("AppSecret", esbApiSetting.getAppSecret()); + return requestObj.toJSONString().getBytes(StandardCharsets.UTF_8); + } + /** + * 检查是否是认证相关的错误(不应该重试) + */ + private boolean isAuthenticationError(int statusCode) { + return statusCode>=400 && statusCode<500; + } + /** + * 发送 token 请求(单次),返回详细结果包括状态码 + */ + private HttpResult invokeTokenRequest() { + HttpResult result = new HttpResult(); + String tokenUrl =esbApiSetting.getServerUrl() + esbApiSetting.getTokenUrl(); + try { + byte[] requestBody = buildTokenRequestBody(); + HttpHeaders headers = buildHeaders(); + // 构建完整的 URL + ResponseEntity requestRes = webProxyService.invokeRequest( + WebProxyService.ESB_API, + HttpMethod.POST, + tokenUrl, + null, + headers, + requestBody, + Duration.ofSeconds(esbApiSetting.getTokenRequestTimeout()), + httpProxyErrorHandler + ).block(); + if(requestRes!=null){ + result.setStatusCode(requestRes.getStatusCode().value()); + result.setResponseHeaders(requestRes.getHeaders()); + byte[] resData = requestRes.getBody(); + if(resData!=null){ + String bodyContent = new String(resData, StandardCharsets.UTF_8); + result.setResponseBody(bodyContent); + if(result.getStatusCode()<200 || result.getStatusCode()>=300){ + result.setErrorMessage(bodyContent); + } + } + }else{ + result.setStatusCode(-1); + result.setErrorMessage("Http request server can not visit."); + } + } catch (Exception e) { + result.setStatusCode(-1); + result.setErrorMessage(e.getMessage()); + result.setExceptionClass(e.getClass().toString()); + logger.error("Failed to retrieve token from url: {}, Exception: {}", + tokenUrl, e.getMessage(), e); + } + return result; + } + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/MGMApiService.java b/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/MGMApiService.java new file mode 100644 index 0000000..4d6e0b3 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/esb/common/services/MGMApiService.java @@ -0,0 +1,123 @@ +package com.mgm.mcrm.apigateway.esb.common.services; + +import com.alibaba.fastjson2.JSONObject; +import com.mgm.mcrm.apigateway.common.dtos.HttpResult; +import com.mgm.mcrm.apigateway.common.dtos.config.EsbApiSetting; +import com.mgm.mcrm.apigateway.esb.common.dtos.EsbToken; +import com.mgm.mcrm.apigateway.proxy.exception.HttpProxyErrorHandler; +import com.mgm.mcrm.apigateway.proxy.services.WebProxyService; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; + +@Service +public class MGMApiService { + private static final String API_GET_USER_INFO="/GetLoginUserInfo"; + private static final String API_GET_ALL_PLAYER_PROFILE="/GetPlayerInfo"; + private final HttpProxyErrorHandler httpProxyErrorHandler=new HttpProxyErrorHandler(); + @Autowired + private EsbApiSetting esbApiSetting; + @Resource + private WebProxyService webProxyService; + @Resource + private EsbTokenService esbTokenService; + + public HttpResult invokeGetAllPlayerProfile(String playerId) throws Exception { + HttpResult result = new HttpResult(); + String apiLink = esbApiSetting.getServerUrl() + API_GET_ALL_PLAYER_PROFILE; + EsbToken esbToken = esbTokenService.getApiAccessToken(); + if(esbToken==null || StringUtils.isBlank(esbToken.getAccessToken())){ + throw new Exception("Invoke ESB token failed"); + } + HttpHeaders headers = new HttpHeaders(); + headers.add("Authorization", esbToken.getAccessToken()); + headers.add("Content-type", "application/json;charset=utf-8"); + headers.add("PlayerId", playerId); + JSONObject requestObj = new JSONObject(); + requestObj.put("playerId", playerId); + byte[] requestBody = requestObj.toJSONString().getBytes(StandardCharsets.UTF_8); + ResponseEntity requestRes = webProxyService.invokeRequest( + WebProxyService.ESB_API, + HttpMethod.GET, + apiLink, + null, + headers, + requestBody, + Duration.ofSeconds(esbApiSetting.getApiRequestTimeout()), + httpProxyErrorHandler + ).block(); + if(requestRes!=null){ + result.setStatusCode(requestRes.getStatusCode().value()); + result.setResponseHeaders(requestRes.getHeaders()); + byte[] resData = requestRes.getBody(); + if(resData!=null){ + String bodyContent = new String(resData, StandardCharsets.UTF_8); + result.setResponseBody(bodyContent); + if(result.getStatusCode()<200 || result.getStatusCode()>=300){ + result.setErrorMessage(bodyContent); + } + } + }else{ + result.setStatusCode(-1); + result.setErrorMessage("Http request server can not visit."); + } + return result; + } + + public HttpResult invokeGetUserInfo(String userId,String userName) throws Exception { + HttpResult result = new HttpResult();; + String apiLink = esbApiSetting.getServerUrl() + API_GET_USER_INFO; + EsbToken esbToken = esbTokenService.getApiAccessToken(); + if(esbToken==null || StringUtils.isBlank(esbToken.getAccessToken())){ + throw new Exception("Invoke ESB token failed"); + } + HttpHeaders headers = new HttpHeaders(); + headers.add("Authorization", esbToken.getAccessToken()); + headers.add("Content-type", "application/json;charset=utf-8"); + JSONObject requestObj = new JSONObject(); + if(StringUtils.isNotBlank(userId)) { + headers.add("UserId", userId); + requestObj.put("userId", userId); + } + if(StringUtils.isNotBlank(userName)) { + headers.add("UserName", userName); + requestObj.put("userName", userName); + } + byte[] requestBody = requestObj.toJSONString().getBytes(StandardCharsets.UTF_8); + ResponseEntity requestRes = webProxyService.invokeRequest( + WebProxyService.ESB_API, + HttpMethod.GET, + apiLink, + null, + headers, + requestBody, + Duration.ofSeconds(esbApiSetting.getApiRequestTimeout()), + httpProxyErrorHandler + ).block(); + if(requestRes!=null){ + result.setStatusCode(requestRes.getStatusCode().value()); + result.setResponseHeaders(requestRes.getHeaders()); + byte[] resData = requestRes.getBody(); + if(resData!=null){ + String bodyContent = new String(resData, StandardCharsets.UTF_8); + result.setResponseBody(bodyContent); + if(result.getStatusCode()<200 || result.getStatusCode()>=300){ + result.setErrorMessage(bodyContent); + } + } + }else{ + result.setStatusCode(-1); + result.setErrorMessage("Http request server can not visit."); + } + return result; + } +} + + diff --git a/src/main/java/com/mgm/mcrm/apigateway/esb/player/services/MgmUserService.java b/src/main/java/com/mgm/mcrm/apigateway/esb/player/services/MgmUserService.java new file mode 100644 index 0000000..3f71baf --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/esb/player/services/MgmUserService.java @@ -0,0 +1,150 @@ +package com.mgm.mcrm.apigateway.esb.player.services; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONReader; +import com.mgm.mcrm.apigateway.basesetting.mapping.models.CfgBlocks; +import com.mgm.mcrm.apigateway.basesetting.mapping.repositories.BlockDao; +import com.mgm.mcrm.apigateway.basesetting.property.models.CfgProperties; +import com.mgm.mcrm.apigateway.basesetting.property.repositories.PropertyDao; +import com.mgm.mcrm.apigateway.basesetting.team.models.CfgDepartments; +import com.mgm.mcrm.apigateway.basesetting.team.models.CfgDivisions; +import com.mgm.mcrm.apigateway.basesetting.team.models.CfgSections; +import com.mgm.mcrm.apigateway.basesetting.team.models.CfgTeamStructure; +import com.mgm.mcrm.apigateway.basesetting.team.repositories.DepartmentDao; +import com.mgm.mcrm.apigateway.basesetting.team.repositories.DivisionDao; +import com.mgm.mcrm.apigateway.basesetting.team.repositories.SectionDao; +import com.mgm.mcrm.apigateway.basesetting.team.repositories.TeamStructureDao; +import com.mgm.mcrm.apigateway.common.constants.SysConstants; +import com.mgm.mcrm.apigateway.common.dtos.HttpResult; +import com.mgm.mcrm.apigateway.common.dtos.ServiceResult; +import com.mgm.mcrm.apigateway.esb.common.dtos.mgmapi.UserInfoApiRecord; +import com.mgm.mcrm.apigateway.esb.common.dtos.mgmapi.UserInfoApiResult; +import com.mgm.mcrm.apigateway.esb.common.services.MGMApiService; +import com.mgm.mcrm.apigateway.system.models.SysGroupPermission; +import com.mgm.mcrm.apigateway.system.models.SysUserView; +import com.mgm.mcrm.apigateway.system.repositories.SysGroupPermissionDao; +import com.mgm.mcrm.apigateway.system.repositories.SysUserViewDao; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class MgmUserService { + + @Resource + private MGMApiService mgmApiService; + @Resource + private PropertyDao propertyDao; + @Resource + private BlockDao blockDao; + @Resource + private DivisionDao divisionDao; + @Resource + private DepartmentDao departmentDao; + @Resource + private SectionDao sectionDao; + @Resource + private TeamStructureDao teamStructureDao; + @Resource + private SysUserViewDao sysUserViewDao; + @Resource + private SysGroupPermissionDao sysGroupPermissionDao; + + public ServiceResult findUserInfoByUserId(String userId) { + ServiceResult result = new ServiceResult(); + try { + HttpResult httpResult = mgmApiService.invokeGetUserInfo(userId,""); + if(httpResult.getStatusCode()==200) { + try { + UserInfoApiResult resultObj = JSONObject.parseObject(httpResult.getResponseBody(),UserInfoApiResult.class,JSONReader.Feature.FieldBased); + if(resultObj!=null && resultObj.getStatusCode()!=null) { + String resultCode = resultObj.getStatusCode(); + if("OK".equals(resultCode)) { + List recrods = resultObj.getRecords(); + if(!recrods.isEmpty()) { + UserInfoApiRecord user = recrods.get(0); + if(!"NULL".equals(user.getUserId())) { + user = this.supplementInfoId(userId,user); + user.setStatusCode(resultObj.getStatusCode()); + user.setStatusDescription(resultObj.getStatusDescription()); + result.setSuccess(true); + result.setReturnData(user); + }else { + result.setSuccess(false); + result.setErrorMessage("User not found"); + } + }else { + result.setSuccess(false); + result.setErrorMessage("User not found"); + } + + }else { + result.setSuccess(false); + result.setErrorMessage("UserInfo API return error:["+resultCode+"] "+resultObj.getStatusDescription()); + } + }else { + result.setSuccess(false); + result.setErrorMessage("Invalid return data"); + } + }catch(Exception err) { + result.setSuccess(false); + result.setErrorMessage("Parse return data failed:"+err.getMessage()); + } + + }else { + result.setSuccess(false); + result.setErrorMessage("Invoke get user info failed:["+httpResult.getStatusCode()+"] "+httpResult.getErrorMessage()); + } + }catch(Exception err) { + result.setSuccess(false); + result.setErrorMessage("Invoke get user info API error:"+err.getMessage()); + } + return result; + } + + + private UserInfoApiRecord supplementInfoId(String userId,UserInfoApiRecord info) { + // 待完善,有可能需要一层一层获得property,division,department,team + CfgProperties property = propertyDao.getByName(info.getProperty()); + if(property!=null) { + info.setPropertyId(property.getId()); + } + CfgBlocks block = blockDao.getByName(info.getBlock()); + if(block!=null) { + info.setBlockId(block.getId()); + } + CfgDivisions division = divisionDao.getByName(info.getDivision()); + if(division!=null) { + info.setDivisionId(division.getId()); + } + CfgDepartments department = departmentDao.getByName(info.getDepartment()); + if(department!=null) { + info.setDepartmentId(department.getId()); + } + CfgSections section = sectionDao.getByName(info.getSection()); + if(section!=null) { + info.setSectionId(section.getId()); + } + CfgTeamStructure team = teamStructureDao.getByDivisionIdAndDepartmentIdAndSectionId(info.getDivisionId(),info.getDepartmentId(),info.getSectionId()); + if(team!=null) { + info.setTeamStructureId(team.getId()); + } + SysUserView userGroup = sysUserViewDao.getByLoginId(userId); + if(userGroup!=null) { + if(userGroup.getGroupId()!=null) { + info.setGroupName(userGroup.getGroupName()); + List permission = sysGroupPermissionDao.getAllByGroupId(userGroup.getGroupId()); + if(!permission.isEmpty()) { + for(SysGroupPermission per:permission) { + if(SysConstants.PERMISSION_APP_VIEW_BOOKING.equals(per.getPermissionId())) info.setCanViewBooking(true); + if(SysConstants.PERMISSION_APP_NEW_BOOKING.equals(per.getPermissionId())) info.setCanNewBooking(true); + if(SysConstants.PERMISSION_APP_TODO_LIST.equals(per.getPermissionId())) info.setCanViewTodoList(true); + if(SysConstants.PERMISSION_APP_DASHBOARD.equals(per.getPermissionId())) info.setCanViewDashboard(true); + } + } + } + } + return info; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/filters/ApiGatewayLoggingFilter.java b/src/main/java/com/mgm/mcrm/apigateway/logging/filters/ApiGatewayLoggingFilter.java new file mode 100644 index 0000000..6f92154 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/logging/filters/ApiGatewayLoggingFilter.java @@ -0,0 +1,175 @@ +package com.mgm.mcrm.apigateway.logging.filters; + + +import com.alibaba.fastjson2.JSONObject; +import com.mgm.mcrm.apigateway.common.utils.NetworkUtil; +import com.mgm.mcrm.apigateway.logging.models.ApiGatewayLog; +import com.mgm.mcrm.apigateway.logging.services.ApiGatewayLogService; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import org.springframework.util.AntPathMatcher; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.web.util.ContentCachingRequestWrapper; +import org.springframework.web.util.ContentCachingResponseWrapper; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +@RequiredArgsConstructor +public class ApiGatewayLoggingFilter extends OncePerRequestFilter { + + private final AntPathMatcher pathMatcher = new AntPathMatcher(); + @Setter + private List excludePatterns; + + private final ApiGatewayLogService apiGatewayLogService; + + @Override + protected void doFilterInternal( + @NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain + ) + throws IOException, ServletException { + Throwable handleException = null; + ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); + ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response); + LocalDateTime startTime = LocalDateTime.now(); + try { + filterChain.doFilter(wrappedRequest, wrappedResponse); + }catch(Exception err){ + handleException = err; + throw err; + } + finally { + String errorMessage = handleException!=null?handleException.getMessage():""; + if(StringUtils.isBlank(errorMessage)){ + errorMessage = checkResponseErrors(wrappedRequest); + } + if(!"OPTIONS".equalsIgnoreCase(request.getMethod())){ + int statusCode = wrappedResponse.getStatus(); + ApiGatewayLog requestLog = createRequestLog(wrappedRequest, startTime); + requestLog.setResponseTime(LocalDateTime.now()); + if(StringUtils.isNotBlank(errorMessage)){ + requestLog.setResponseBody(errorMessage); + }else{ + requestLog.setResponseBody(getResponseContent(wrappedResponse)); + } + requestLog.setStatusCode(String.valueOf(statusCode)); + apiGatewayLogService.saveApiGatewayLog(requestLog); + } + wrappedResponse.copyBodyToResponse(); + } + } + + private String checkResponseErrors(HttpServletRequest request) { + String errorMessage = ""; + Object exceptionVal = request.getAttribute("AUTH_EXCEPTION"); + errorMessage = exceptionVal!=null?exceptionVal.toString():""; + if(StringUtils.isBlank(errorMessage)){ + exceptionVal = request.getAttribute(DispatcherServlet.EXCEPTION_ATTRIBUTE); + errorMessage = exceptionVal!=null?exceptionVal.toString():""; + } + return errorMessage; + } + + private ApiGatewayLog createRequestLog(HttpServletRequest request, LocalDateTime startTime) { + ApiGatewayLog log = new ApiGatewayLog(); + log.setRequestMethod(request.getMethod()); + String requestUrl =getFullUrl(request); + log.setUrl(requestUrl); + log.setRequestTime(startTime); + log.setHeader(getHeadersAsString(request)); + log.setRequestSource(getRequestSource(request)); + if (request instanceof ContentCachingRequestWrapper wrappedRequest) { + byte[] content = wrappedRequest.getContentAsByteArray(); + if (content.length > 0) { + try { + String body = new String(content, wrappedRequest.getCharacterEncoding()); + log.setRequestBody(body); + }catch(Exception ignored){ + log.setRequestBody(new String(content)); + } + } + } + return log; + } + + private String getFullUrl(HttpServletRequest request) { + String scheme = request.getScheme(); + String serverName = request.getServerName(); + int serverPort = request.getServerPort(); + String contextPath = request.getContextPath(); + String servletPath = request.getServletPath(); + String pathInfo = request.getPathInfo(); + String queryString = request.getQueryString(); + + StringBuilder url = new StringBuilder(); + url.append(scheme).append("://").append(serverName); + if ((scheme.equals("http") && serverPort != 80) || + (scheme.equals("https") && serverPort != 443)) { + url.append(":").append(serverPort); + } + url.append(contextPath).append(servletPath); + if (pathInfo != null) { + url.append(pathInfo); + } + if (StringUtils.isNotBlank(queryString)) { + url.append("?").append(queryString); + } + return url.toString(); + } + + private String getRequestSource(HttpServletRequest request){ + Map requestSource = new HashMap(); + requestSource.put("requestIP", NetworkUtil.getClientIpAddr(request)); + return JSONObject.toJSONString(requestSource); + } + + private String getResponseContent(HttpServletResponse response) { + String responseBody=""; + if (response instanceof ContentCachingResponseWrapper wrappedResponse) { + byte[] content = wrappedResponse.getContentAsByteArray(); + if (content.length > 0) { + try { + responseBody = new String(content, wrappedResponse.getCharacterEncoding()); + }catch(Exception ignored){ + responseBody = new String(content); + } + } + } + return responseBody; + } + + private String getHeadersAsString(HttpServletRequest request) { + StringBuilder headers = new StringBuilder(); + request.getHeaderNames().asIterator().forEachRemaining(headerName -> { + headers.append(headerName).append(": ").append(request.getHeader(headerName)).append("; "); + }); + return headers.toString(); + } + + @Override + protected boolean shouldNotFilter(@NonNull HttpServletRequest request) { + if (excludePatterns == null) { + return false; + } + return excludePatterns.stream() + .anyMatch(pattern -> pathMatcher.match(pattern, request.getRequestURI())); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/filters/WebhookLoggingFilter.java b/src/main/java/com/mgm/mcrm/apigateway/logging/filters/WebhookLoggingFilter.java new file mode 100644 index 0000000..48ebdf3 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/logging/filters/WebhookLoggingFilter.java @@ -0,0 +1,162 @@ +package com.mgm.mcrm.apigateway.logging.filters; + + +import com.alibaba.fastjson2.JSONObject; + +import com.mgm.mcrm.apigateway.logging.models.WebhookLog; +import com.mgm.mcrm.apigateway.logging.services.WebhookLogService; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import org.springframework.web.servlet.DispatcherServlet; +import org.springframework.web.util.ContentCachingRequestWrapper; +import org.springframework.web.util.ContentCachingResponseWrapper; + +import java.io.IOException; +import java.time.LocalDateTime; + +@Component +@RequiredArgsConstructor +public class WebhookLoggingFilter extends OncePerRequestFilter { + + private final WebhookLogService webhookLogService; + + @Override + protected void doFilterInternal( + @NonNull HttpServletRequest request, + @NonNull HttpServletResponse response, + @NonNull FilterChain filterChain + ) + throws IOException, ServletException { + Throwable handleException = null; + ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); + ContentCachingResponseWrapper wrappedResponse = new ContentCachingResponseWrapper(response); + LocalDateTime startTime = LocalDateTime.now(); + try { + filterChain.doFilter(wrappedRequest, wrappedResponse); + }catch(Exception err){ + handleException = err; + throw err; + } + finally { + String errorMessage = handleException!=null?handleException.getMessage():""; + if(StringUtils.isBlank(errorMessage)){ + errorMessage = checkResponseErrors(wrappedRequest); + } + if(!"OPTIONS".equalsIgnoreCase(request.getMethod())){ + int statusCode = wrappedResponse.getStatus(); + WebhookLog requestLog = createRequestLog(wrappedRequest, startTime); + requestLog.setResponseTime(LocalDateTime.now()); + if(StringUtils.isNotBlank(errorMessage)){ + requestLog.setResponseBody(errorMessage); + }else{ + requestLog.setResponseBody(getResponseContent(wrappedResponse)); + requestLog.setSuccess(false); + try{ + JSONObject resultObj = JSONObject.parseObject(requestLog.getResponseBody()); + if(resultObj.containsKey("success")){ + requestLog.setSuccess(resultObj.getBoolean("success")); + } + }catch(Exception ignored){ + + } + } + requestLog.setStatusCode(String.valueOf(statusCode)); + webhookLogService.saveWebhookLog(requestLog); + } + wrappedResponse.copyBodyToResponse(); + } + } + + private String checkResponseErrors(HttpServletRequest request) { + String errorMessage = ""; + Object exceptionVal = request.getAttribute("AUTH_EXCEPTION"); + errorMessage = exceptionVal!=null?exceptionVal.toString():""; + if(StringUtils.isBlank(errorMessage)){ + exceptionVal = request.getAttribute(DispatcherServlet.EXCEPTION_ATTRIBUTE); + errorMessage = exceptionVal!=null?exceptionVal.toString():""; + } + return errorMessage; + } + + private WebhookLog createRequestLog(HttpServletRequest request, LocalDateTime startTime) { + WebhookLog log = new WebhookLog(); + log.setRequestMethod(request.getMethod()); + log.setUrl(getFullUrl(request)); + log.setRequestTime(startTime); + log.setHeader(getHeadersAsString(request)); + String messageId = request.getHeader("Ac-Events-Message-Id"); + String propertyId = request.getHeader("Ac-Property-Id"); + String eventType = request.getHeader("Ac-Events-Type"); + log.setMessageId(messageId); + log.setEventType(eventType); + log.setPropertyId(propertyId); + log.setSuccess(false); + if (request instanceof ContentCachingRequestWrapper wrappedRequest) { + byte[] content = wrappedRequest.getContentAsByteArray(); + if (content.length > 0) { + try { + String body = new String(content, wrappedRequest.getCharacterEncoding()); + log.setRequestBody(body); + }catch(Exception ignored){ + log.setRequestBody(new String(content)); + } + } + } + return log; + } + + private String getFullUrl(HttpServletRequest request) { + String scheme = request.getScheme(); + String serverName = request.getServerName(); + int serverPort = request.getServerPort(); + String contextPath = request.getContextPath(); + String servletPath = request.getServletPath(); + String pathInfo = request.getPathInfo(); + String queryString = request.getQueryString(); + + StringBuilder url = new StringBuilder(); + url.append(scheme).append("://").append(serverName); + if ((scheme.equals("http") && serverPort != 80) || + (scheme.equals("https") && serverPort != 443)) { + url.append(":").append(serverPort); + } + url.append(contextPath).append(servletPath); + if (pathInfo != null) { + url.append(pathInfo); + } + if (StringUtils.isNotBlank(queryString)) { + url.append("?").append(queryString); + } + return url.toString(); + } + + private String getResponseContent(HttpServletResponse response) { + String responseBody=""; + if (response instanceof ContentCachingResponseWrapper wrappedResponse) { + byte[] content = wrappedResponse.getContentAsByteArray(); + if (content.length > 0) { + try { + responseBody = new String(content, wrappedResponse.getCharacterEncoding()); + }catch(Exception ignored){ + responseBody = new String(content); + } + } + } + return responseBody; + } + + private String getHeadersAsString(HttpServletRequest request) { + StringBuilder headers = new StringBuilder(); + request.getHeaderNames().asIterator().forEachRemaining(headerName -> { + headers.append(headerName).append(": ").append(request.getHeader(headerName)).append("; "); + }); + return headers.toString(); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/models/ApiGatewayLog.java b/src/main/java/com/mgm/mcrm/apigateway/logging/models/ApiGatewayLog.java new file mode 100644 index 0000000..48ea9d7 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/logging/models/ApiGatewayLog.java @@ -0,0 +1,43 @@ +package com.mgm.mcrm.apigateway.logging.models; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@Entity +@Table(name="api_gateway_logs") +public class ApiGatewayLog { + + @Id + @Column(name = "api_log_id",nullable = false) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long apiLogId; + @Column(name = "url") + private String url; + @Column(name = "category") + private String category; + @Column(name = "request_method") + private String requestMethod; + @Column(name = "request_dtm") + private LocalDateTime requestTime; + @Column(name = "response_dtm") + private LocalDateTime responseTime; + @Column(name = "header") + private String header=""; + @Column(name = "request_body") + private String requestBody=""; + @Column(name = "request_source") + private String requestSource; + @Column(name = "response_body") + private String responseBody=""; + @Column(name = "success_flag") + private boolean successFlag=false; + @Column(name = "status_code") + private String statusCode=""; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/models/WebhookLog.java b/src/main/java/com/mgm/mcrm/apigateway/logging/models/WebhookLog.java new file mode 100644 index 0000000..19bfb59 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/logging/models/WebhookLog.java @@ -0,0 +1,46 @@ +package com.mgm.mcrm.apigateway.logging.models; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@Entity +@Table(name="webhook_logs") +public class WebhookLog { + + @Id + @Column(name = "webhook_log_id",nullable = false) + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long webhookLogId; + @Column(name = "message_id") + private String messageId; + @Column(name = "property_id") + private String propertyId; + @Column(name = "event_type") + private String eventType; + @Column(name = "url") + private String url; + @Column(name = "request_method") + private String requestMethod; + @Column(name = "request_time") + private LocalDateTime requestTime; + @Column(name = "response_time") + private LocalDateTime responseTime; + @Column(name = "header") + private String header=""; + @Column(name = "request_body") + private String requestBody=""; + @Column(name = "response_body") + private String responseBody=""; + @Column(name = "success") + private Boolean success; + @Column(name = "status_code") + private String statusCode=""; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/repositories/ApiGatewayLogDao.java b/src/main/java/com/mgm/mcrm/apigateway/logging/repositories/ApiGatewayLogDao.java new file mode 100644 index 0000000..8e758e3 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/logging/repositories/ApiGatewayLogDao.java @@ -0,0 +1,14 @@ +package com.mgm.mcrm.apigateway.logging.repositories; + +import com.mgm.mcrm.apigateway.logging.models.ApiGatewayLog; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ApiGatewayLogDao extends JpaRepository { + + Page findAll(Specification listSpec, Pageable pageable); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/repositories/WebhookLogDao.java b/src/main/java/com/mgm/mcrm/apigateway/logging/repositories/WebhookLogDao.java new file mode 100644 index 0000000..761c829 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/logging/repositories/WebhookLogDao.java @@ -0,0 +1,16 @@ +package com.mgm.mcrm.apigateway.logging.repositories; + +import com.mgm.mcrm.apigateway.logging.models.WebhookLog; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface WebhookLogDao extends JpaRepository { + + Page findAll(Specification listSpec, Pageable pageable); + + int countByMessageId(String messageId); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/services/ApiGatewayLogService.java b/src/main/java/com/mgm/mcrm/apigateway/logging/services/ApiGatewayLogService.java new file mode 100644 index 0000000..e474e62 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/logging/services/ApiGatewayLogService.java @@ -0,0 +1,29 @@ +package com.mgm.mcrm.apigateway.logging.services; + +import com.mgm.mcrm.apigateway.logging.models.ApiGatewayLog; +import com.mgm.mcrm.apigateway.logging.repositories.ApiGatewayLogDao; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ApiGatewayLogService { + private final Logger logger = LoggerFactory.getLogger(ApiGatewayLogService.class); + + private final ApiGatewayLogDao apiGatewayLogDao; + @Async + public void saveApiGatewayLog(ApiGatewayLog requestLog) { + try { + if (requestLog.getRequestBody() == null) { + requestLog.setRequestBody(""); + } + apiGatewayLogDao.save(requestLog); + } catch (Exception e) { + logger.error("Failed to save api gateway log to database with error:{}", e.getMessage()); + } + } + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/logging/services/WebhookLogService.java b/src/main/java/com/mgm/mcrm/apigateway/logging/services/WebhookLogService.java new file mode 100644 index 0000000..0f5cbeb --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/logging/services/WebhookLogService.java @@ -0,0 +1,34 @@ +package com.mgm.mcrm.apigateway.logging.services; + +import com.mgm.mcrm.apigateway.logging.models.WebhookLog; +import com.mgm.mcrm.apigateway.logging.repositories.WebhookLogDao; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; + + +@Service +@RequiredArgsConstructor +public class WebhookLogService { + private final Logger logger = LoggerFactory.getLogger(WebhookLogService.class); + + private final WebhookLogDao webhookLogDao; + + @Async + public void saveWebhookLog(WebhookLog requestLog) { + try { + if (requestLog.getRequestBody() == null) { + requestLog.setRequestBody(""); + } + webhookLogDao.save(requestLog); + } catch (Exception e) { + logger.error("Failed to save webhook log to database with error:{}", e.getMessage()); + } + } + + public boolean isMessageDuplicated(String messageId) { + return webhookLogDao.countByMessageId(messageId)>0; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/notification/controllers/NotificationController.java b/src/main/java/com/mgm/mcrm/apigateway/notification/controllers/NotificationController.java new file mode 100644 index 0000000..f2f040d --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/notification/controllers/NotificationController.java @@ -0,0 +1,72 @@ +package com.mgm.mcrm.apigateway.notification.controllers; + +import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; +import com.mgm.mcrm.apigateway.common.dtos.config.EmailProxySetting; +import com.mgm.mcrm.apigateway.notification.dtos.EmailProxyRequest; +import com.mgm.mcrm.apigateway.notification.dtos.SendEmailRequest; +import com.mgm.mcrm.apigateway.notification.services.EmailNotificationService; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@Tag(name = "Notification", description = "APIs to Send notifications") +@RestController +@RequestMapping("/api/notification") +public class NotificationController { + private final Logger logger = LoggerFactory.getLogger(NotificationController.class); + @Resource + private EmailNotificationService emailNotificationService; + @Autowired + private EmailProxySetting emailProxySetting; + + @PostMapping(value = "/send_email", produces = "application/json;charset=UTF-8") + public ResponseEntity handleSendEmailRequest( + @RequestBody(required = true) SendEmailRequest requestData, + HttpServletRequest request + ){ + + String appId = request.getHeader("application-id"); + String securityKey = request.getHeader("authorization"); + + BaseApiResponse result = emailNotificationService.sendEmailByRequest(requestData,emailProxySetting); + return ResponseEntity.status(HttpStatus.OK).body(result); + } + + /** + * 接收后端A转发过来的邮件请求,字段名与 SendEmailRequest 不同,这里做转换。 + * showTemp 为 HTML 正文,优先于 content;有 cc 时按群发模式发送。 + */ + @PostMapping(value = "/proxy/send_email", produces = "application/json;charset=UTF-8") + public ResponseEntity handleSendEmailProxyRequest( + @RequestBody(required = true) EmailProxyRequest requestData, + HttpServletRequest request + ) { + logger.info("handleSendEmailProxyRequest start, id={}, subject={}, to={}, cc={}, sender={}", + requestData.getId(), requestData.getSubject(), requestData.getTo(), + requestData.getCc(), requestData.getSender()); + SendEmailRequest sendEmailRequest = new SendEmailRequest(); + sendEmailRequest.setSendTo(requestData.getTo()); + sendEmailRequest.setCc(requestData.getCc()); + sendEmailRequest.setSubject(requestData.getSubject()); + String bodyContent = StringUtils.isNotBlank(requestData.getShowTemp()) + ? requestData.getShowTemp() + : requestData.getContent(); + sendEmailRequest.setBodyContent(bodyContent); + sendEmailRequest.setContentType("HTML"); + sendEmailRequest.setSender(requestData.getSender()); + // 有 cc 时用群发模式(false),让 to + cc 在同一封邮件里 + sendEmailRequest.setSendSeparately(StringUtils.isBlank(requestData.getCc())); + + BaseApiResponse result = emailNotificationService.sendEmailByRequest(sendEmailRequest, emailProxySetting); + logger.info("handleSendEmailProxyRequest end, id={}, success={}, statusCode={}, message={}", + requestData.getId(), result.isSuccess(), result.getStatusCode(), result.getMessage()); + return ResponseEntity.status(HttpStatus.OK).body(result); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/notification/dtos/EmailProxyRequest.java b/src/main/java/com/mgm/mcrm/apigateway/notification/dtos/EmailProxyRequest.java new file mode 100644 index 0000000..972d10f --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/notification/dtos/EmailProxyRequest.java @@ -0,0 +1,29 @@ +package com.mgm.mcrm.apigateway.notification.dtos; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import lombok.Data; + +/** + * 后端A转发过来的发送邮件参数。 + * showTemp 为 HTML 正文,content 为纯文本备用正文。 + */ +@Data +@JsonIgnoreProperties(ignoreUnknown = true) +public class EmailProxyRequest { + private int id; + private String name; + private int type; + private String field; + private String bookIds; + private int propertyId; + private String subject; + private String content; + private String showTemp; + private String sender; + private String to; + private String cc; + private String createdBy; + private String updatedBy; + private String createdAt; + private String updatedAt; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/notification/dtos/SendEmailRequest.java b/src/main/java/com/mgm/mcrm/apigateway/notification/dtos/SendEmailRequest.java new file mode 100644 index 0000000..4413dce --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/notification/dtos/SendEmailRequest.java @@ -0,0 +1,19 @@ +package com.mgm.mcrm.apigateway.notification.dtos; + +import lombok.Data; + +@Data +public class SendEmailRequest { + private String sendTo; + private String cc; + private String contentType="TEXT"; + private String contentCharset="UTF-8"; + private String subject; + private String bodyContent; + private String fileName; + private String dataContent; + private String sender; + private String senderPassword; + private boolean sendSeparately=true; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/notification/services/EmailNotificationService.java b/src/main/java/com/mgm/mcrm/apigateway/notification/services/EmailNotificationService.java new file mode 100644 index 0000000..51cbfe9 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/notification/services/EmailNotificationService.java @@ -0,0 +1,102 @@ +package com.mgm.mcrm.apigateway.notification.services; + +import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; +import com.mgm.mcrm.apigateway.common.dtos.config.EmailProxySetting; +import com.mgm.mcrm.apigateway.notification.dtos.SendEmailRequest; +import jakarta.mail.Session; +import jakarta.mail.internet.MimeMessage; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Service; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +@Service +public class EmailNotificationService { + private final Logger logger = LoggerFactory.getLogger(EmailNotificationService.class); + + public BaseApiResponse sendEmailByRequest(SendEmailRequest requestData, EmailProxySetting emailConfig) { + BaseApiResponse result = new BaseApiResponse(); + try { + JavaMailSenderImpl emailSender = new JavaMailSenderImpl(); + emailSender.setHost(emailConfig.getSmtpServer()); + emailSender.setPort(emailConfig.getSmtpPort()); + try{ + Charset.forName(requestData.getContentCharset()); + emailSender.setDefaultEncoding(requestData.getContentCharset()); + }catch(Exception ignored){ + emailSender.setDefaultEncoding("UTF-8"); + } + if(StringUtils.isNotBlank(requestData.getSender()) && StringUtils.isNotBlank(requestData.getSenderPassword())){ + emailSender.setUsername(requestData.getSender()); + emailSender.setPassword(requestData.getSenderPassword()); + }else{ + emailSender.setUsername(emailConfig.getUsername()); + emailSender.setPassword(emailConfig.getPassword()); + } + Properties props = new Properties(); + props.put("mail.smtp.auth", emailConfig.isAuth()); + props.put("mail.smtp.starttls.enable", emailConfig.isStarttls()); + Session session = Session.getInstance(props); + emailSender.setSession(session); + MimeMessage mail = emailSender.createMimeMessage(); + MimeMessageHelper helper = new MimeMessageHelper(mail, true); + helper.setSubject(requestData.getSubject()==null?"":requestData.getSubject()); + helper.setText(requestData.getBodyContent()==null?"":requestData.getBodyContent(), "HTML".equalsIgnoreCase(requestData.getContentType())); + String fromAddress = StringUtils.isNotBlank(requestData.getSender()) ? requestData.getSender() : emailConfig.getUsername(); + if (StringUtils.isNotBlank(fromAddress)) { + helper.setFrom(fromAddress); + } + if (StringUtils.isNotBlank(requestData.getCc())) { + List ccList = new ArrayList<>(); + for (String addr : requestData.getCc().split("[,;]")) { + if (StringUtils.isNotBlank(addr)) { + ccList.add(addr.trim()); + } + } + if (!ccList.isEmpty()) { + helper.setCc(ccList.toArray(new String[0])); + } + } + String sendToList = StringUtils.isNotBlank(requestData.getSendTo())? requestData.getSendTo() : emailConfig.getDefaultSendTo(); + String[] toAddresses = sendToList.split("[,;]"); + if(requestData.isSendSeparately()){ + for (String address : toAddresses) { + helper.setTo(address); + try { + emailSender.send(mail); + result.setSuccess(true); + result.setStatusCode(200); + } catch (Exception err) { + result.setMessage("Send Email to ["+address+"] in separate mode failed:"+err.getMessage()); + result.setSuccess(false); + result.setStatusCode(-1); + } + } + }else{ + helper.setTo(toAddresses); + try { + emailSender.send(mail); + result.setSuccess(true); + result.setStatusCode(200); + } catch (Exception err) { + result.setMessage("Send Email to ["+sendToList+"] in batch mode failed:"+err.getMessage()); + result.setSuccess(false); + result.setStatusCode(-1); + } + } + + }catch(Exception err){ + result.setSuccess(false); + result.setMessage("Send email failed with error:"+err.getMessage()); + result.setStatusCode(-1); + } + return result; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/player/services/PlayerService.java b/src/main/java/com/mgm/mcrm/apigateway/player/services/PlayerService.java new file mode 100644 index 0000000..0b4b8bd --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/player/services/PlayerService.java @@ -0,0 +1,103 @@ +package com.mgm.mcrm.apigateway.player.services; + +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.JSONReader; +import com.mgm.mcrm.apigateway.appsetting.services.SysSettingService; +import com.mgm.mcrm.apigateway.common.dtos.HttpResult; +import com.mgm.mcrm.apigateway.common.dtos.ServiceResult; +import com.mgm.mcrm.apigateway.esb.common.dtos.mgmapi.PlayerProfileApiResult; +import com.mgm.mcrm.apigateway.esb.common.services.MGMApiService; +import com.mgm.mcrm.apigateway.reservation.services.OperaDBBookingService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Slf4j +@Service +public class PlayerService { + + @Resource + private MGMApiService mgmApiService; + + @Resource + private SysSettingService sysSettingService; + + @Resource + private OperaDBBookingService operaDBBookingService; + + + public ServiceResult findWebPlayerProfileByPlayerId(String playerId) { + ServiceResult result = new ServiceResult(); + HttpResult httpResult; + try { + httpResult = mgmApiService.invokeGetAllPlayerProfile(playerId); + if (httpResult.getStatusCode() != 200) { + result.setSuccess(false); + result.setErrorMessage("Invoke find player profile Failed:[" + httpResult.getStatusCode() + "] " + httpResult.getErrorMessage()); + return result; + } + } catch (Exception err) { + result.setSuccess(false); + result.setErrorMessage("Invoke find player profile API error:" + err.getMessage()); + return result; + } + + PlayerProfileApiResult resultObj; + try { + resultObj = JSONObject.parseObject( + httpResult.getResponseBody(), + PlayerProfileApiResult.class, + JSONReader.Feature.FieldBased + ); + + if (resultObj == null || resultObj.getStatusCode() == null) { + result.setSuccess(false); + result.setErrorMessage("Invalid return data"); + return result; + } + + } catch (Exception err) { + result.setSuccess(false); + result.setErrorMessage("Parse return data failed:" + err.getMessage()); + return result; + } + + String resultCode = resultObj.getStatusCode(); + if (!"OK".equals(resultCode)) { + result.setSuccess(false); + result.setErrorMessage(resultObj.getStatusDescription()); + return result; + } + + if (StringUtils.isNotEmpty(resultObj.getPlayerId())) { + resultObj.setName(resultObj.getEnglishName()); + this.supplementProfileNameIds(resultObj); + result.setSuccess(true); + result.setReturnData(resultObj); + } else { + result.setSuccess(false); + result.setErrorMessage("Player not found"); + } + return result; + } + + private void supplementProfileNameIds(PlayerProfileApiResult profile) { + boolean isMock = sysSettingService.isMockReplicate(); + if (isMock) { + List lists = new ArrayList<>(); + lists.add(710825); + profile.setNameIds(lists); + } else { + List lists = operaDBBookingService.getOperaNameIDByPlayerID(profile.getPlayerId()); + List nameList = new ArrayList<>(); + if (lists != null && !lists.isEmpty()) { + nameList.add(Integer.valueOf(lists.getFirst())); + } + profile.setNameIds(nameList); + } + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/config/WebClientConfig.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/config/WebClientConfig.java new file mode 100644 index 0000000..c0bf39c --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/config/WebClientConfig.java @@ -0,0 +1,273 @@ +package com.mgm.mcrm.apigateway.proxy.config; + +import com.mgm.mcrm.apigateway.common.dtos.config.DaylightApiSetting; +import com.mgm.mcrm.apigateway.common.dtos.config.EsbApiSetting; +import com.mgm.mcrm.apigateway.common.dtos.config.OperaApiSetting; +import io.netty.channel.ChannelOption; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; +import io.netty.handler.ssl.util.InsecureTrustManagerFactory; +import io.netty.handler.timeout.ReadTimeoutHandler; +import io.netty.handler.timeout.WriteTimeoutHandler; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.ssl.SslBundle; +import org.springframework.boot.ssl.SslBundles; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.reactive.ReactorClientHttpConnector; +import org.springframework.web.reactive.function.client.ExchangeFilterFunction; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; +import reactor.netty.http.client.HttpClient; +import reactor.netty.resources.ConnectionProvider; +import reactor.netty.transport.ProxyProvider; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +@Configuration +public class WebClientConfig { + + @Value("${mcrm.proxy.max-connection-pool:100}") + private int maxConnectionPool; + + @Value("${mcrm.proxy.connect-timeout:10}") + private int connectTimeout; + + @Value("${mcrm.proxy.read-timeout:10}") + private int readTimeout; + + private final int maxMemoryCacheM = 30; + + @Autowired + private DaylightApiSetting daylightApiSetting; + @Autowired + private EsbApiSetting esbApiSetting; + @Autowired + private OperaApiSetting operaApiSetting; + @Autowired + private SslBundles sslBundles; + + private final Logger logger = LoggerFactory.getLogger(WebClientConfig.class); + + + + @Bean + public WebClient daylightWebClient() { + ConnectionProvider provider = ConnectionProvider.builder("proxy-pool") + .maxConnections(maxConnectionPool) + .pendingAcquireTimeout(Duration.ofSeconds(60)) + .maxIdleTime(Duration.ofSeconds(30)) + .build(); + + HttpClient httpClient = HttpClient.create(provider) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout*1000) + .responseTimeout(Duration.ofSeconds(readTimeout)) + .secure(spec -> { + try { + spec.sslContext(this.createDaylightSslContent()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }) + .doOnConnected(conn -> { + logger.info("Daylight Connected using proxy: {}", conn.channel().remoteAddress()); + conn.addHandlerLast(new ReadTimeoutHandler(readTimeout, TimeUnit.SECONDS)) + .addHandlerLast(new WriteTimeoutHandler(readTimeout, TimeUnit.SECONDS)); + }) + .doOnRequest((request, connection) -> + logger.info("Daylight Request via proxy: {}", request) + ); + if(StringUtils.isNotBlank(daylightApiSetting.getProxyHost())){ + httpClient = httpClient.proxy(this::initDaylightProxy); + } + + return WebClient.builder() + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .codecs(configurer -> { + configurer.defaultCodecs().maxInMemorySize(maxMemoryCacheM * 1024 * 1024); + }) + .filter(retryFilter()) + .build(); + } + + @Bean + public WebClient esbWebClient() { + ConnectionProvider provider = ConnectionProvider.builder("proxy-pool") + .maxConnections(maxConnectionPool) + .pendingAcquireTimeout(Duration.ofSeconds(60)) + .maxIdleTime(Duration.ofSeconds(30)) + .build(); + + HttpClient httpClient = HttpClient.create(provider) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout*1000) + .responseTimeout(Duration.ofSeconds(readTimeout)) + .secure(spec -> { + try { + spec.sslContext(this.createEsbSslContent()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }) + .doOnConnected(conn -> { + logger.info("ESB Connected using proxy: {}", conn.channel().remoteAddress()); + conn.addHandlerLast(new ReadTimeoutHandler(readTimeout, TimeUnit.SECONDS)) + .addHandlerLast(new WriteTimeoutHandler(readTimeout, TimeUnit.SECONDS)); + }) + .doOnRequest((request, connection) -> + logger.info("ESB Request via proxy: {}", request) + ); + if(StringUtils.isNotBlank(esbApiSetting.getProxyHost())){ + httpClient = httpClient.proxy(this::initEsbProxy); + } + + return WebClient.builder() + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .codecs(configurer -> { + configurer.defaultCodecs().maxInMemorySize(maxMemoryCacheM * 1024 * 1024); + }) + .filter(retryFilter()) + .build(); + } + + @Bean + public WebClient operaWebClient() { + ConnectionProvider provider = ConnectionProvider.builder("proxy-pool") + .maxConnections(maxConnectionPool) + .pendingAcquireTimeout(Duration.ofSeconds(60)) + .maxIdleTime(Duration.ofSeconds(30)) + .build(); + + HttpClient httpClient = HttpClient.create(provider) + .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeout*1000) + .responseTimeout(Duration.ofSeconds(readTimeout)) + .secure(spec -> { + try { + spec.sslContext(this.createOperaSslContent()); + } catch (Exception e) { + throw new RuntimeException(e); + } + }) + .doOnConnected(conn -> { + logger.info("Opera Connected using proxy: {}", conn.channel().remoteAddress()); + conn.addHandlerLast(new ReadTimeoutHandler(readTimeout, TimeUnit.SECONDS)) + .addHandlerLast(new WriteTimeoutHandler(readTimeout, TimeUnit.SECONDS)); + }) + .doOnRequest((request, connection) -> + logger.info("Opera Request via proxy: {}", request) + ); + if(StringUtils.isNotBlank(operaApiSetting.getProxyHost())){ + httpClient = httpClient.proxy(this::initOperaProxy); + } + + return WebClient.builder() + .clientConnector(new ReactorClientHttpConnector(httpClient)) + .codecs(configurer -> { + configurer.defaultCodecs().maxInMemorySize(maxMemoryCacheM * 1024 * 1024); + }) + .filter(retryFilter()) + .build(); + } + // retry filter + private ExchangeFilterFunction retryFilter() { + return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> { + if (clientResponse.statusCode().is5xxServerError()) { + return clientResponse.mutate() + .build() + .bodyToMono(String.class) + .flatMap(body -> { + return Mono.error(new RuntimeException("Server error: " + clientResponse.statusCode())); + }); + } + return Mono.just(clientResponse); + }); + } + //-- Daylight init + private SslContext createDaylightSslContent() throws SSLException { + if(StringUtils.isBlank(daylightApiSetting.getSslBundleName())){ + return this.createNoVerifySslContext(); + }else{ + return this.createSslContextByConfig(daylightApiSetting.getSslBundleName()); + } + } + + private void initDaylightProxy(ProxyProvider.TypeSpec proxy){ + this.setProxySettingByConfig( + proxy, + daylightApiSetting.getProxyHost(), + daylightApiSetting.getProxyPort(), + daylightApiSetting.getProxyUserName(), + daylightApiSetting.getProxyPassword() + ); + } + //-- Opera init + private SslContext createOperaSslContent() throws SSLException { + if(StringUtils.isBlank(operaApiSetting.getSslBundleName())){ + return this.createNoVerifySslContext(); + }else{ + return this.createSslContextByConfig(operaApiSetting.getSslBundleName()); + } + } + + private void initOperaProxy(ProxyProvider.TypeSpec proxy){ + this.setProxySettingByConfig( + proxy, + operaApiSetting.getProxyHost(), + operaApiSetting.getProxyPort(), + operaApiSetting.getProxyUserName(), + operaApiSetting.getProxyPassword() + ); + } + //-- Esb init + private SslContext createEsbSslContent() throws SSLException { + if(StringUtils.isBlank(esbApiSetting.getSslBundleName())){ + return this.createNoVerifySslContext(); + }else{ + return this.createSslContextByConfig(esbApiSetting.getSslBundleName()); + } + } + + private void initEsbProxy(ProxyProvider.TypeSpec proxy){ + this.setProxySettingByConfig( + proxy, + esbApiSetting.getProxyHost(), + esbApiSetting.getProxyPort(), + esbApiSetting.getProxyUserName(), + esbApiSetting.getProxyPassword() + ); + } + + //---------- + private void setProxySettingByConfig(ProxyProvider.TypeSpec proxy, String proxyHost, int proxyPort, String authUserName, String authPassword){ + if (StringUtils.isNotBlank(authUserName) && StringUtils.isNotBlank(authPassword)) { + proxy.type(ProxyProvider.Proxy.HTTP) + .host(proxyHost) + .port(proxyPort) + .username(authUserName) + .password(s->authPassword); + }else{ + proxy.type(ProxyProvider.Proxy.HTTP) + .host(proxyHost) + .port(proxyPort); + } + } + + private SslContext createNoVerifySslContext() throws SSLException { + return SslContextBuilder.forClient() + .trustManager(InsecureTrustManagerFactory.INSTANCE).build(); + } + + private SslContext createSslContextByConfig(String sslBundleName) throws SSLException { + SslBundle sslBundle = sslBundles.getBundle(sslBundleName); + SSLContext netSslContext = sslBundle.createSslContext(); + return SslContextBuilder.forClient() + .sslContextProvider(netSslContext.getProvider()) + .build(); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/WebProxyController.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/WebProxyController.java new file mode 100644 index 0000000..fabfb8e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/WebProxyController.java @@ -0,0 +1,173 @@ +package com.mgm.mcrm.apigateway.proxy.controllers; + +import com.mgm.mcrm.apigateway.appsetting.services.SysSettingService; +import com.mgm.mcrm.apigateway.common.constants.SysCommon; +import com.mgm.mcrm.apigateway.common.dtos.config.DaylightApiSetting; +import com.mgm.mcrm.apigateway.common.dtos.config.EsbApiSetting; +import com.mgm.mcrm.apigateway.common.dtos.config.OperaApiSetting; +import com.mgm.mcrm.apigateway.daylight.common.services.DaylightTokenService; +import com.mgm.mcrm.apigateway.daylight.common.dtos.TokenResponse; +import com.mgm.mcrm.apigateway.esb.common.dtos.EsbToken; +import com.mgm.mcrm.apigateway.esb.common.services.EsbTokenService; +import com.mgm.mcrm.apigateway.proxy.exception.HttpProxyErrorHandler; +import com.mgm.mcrm.apigateway.proxy.exception.SoapProxyErrorHandler; +import com.mgm.mcrm.apigateway.proxy.services.WebProxyService; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.time.Duration; +import java.util.*; + +@Tag(name = "Proxy", description = "Controller for API proxy") +@RestController +public class WebProxyController { + private final Logger logger = LoggerFactory.getLogger(WebProxyController.class); + private final HttpProxyErrorHandler httpErrorHandler= new HttpProxyErrorHandler(); + private final SoapProxyErrorHandler soapProxyErrorHandler = new SoapProxyErrorHandler(); + @Resource + private WebProxyService webProxyService; + @Resource + private DaylightTokenService daylightTokenService; + @Resource + private EsbTokenService esbTokenService; + @Autowired + private DaylightApiSetting daylightApiSetting; + @Autowired + private EsbApiSetting esbApiSetting; + @Autowired + private OperaApiSetting operaApiSetting; + + @Resource + private SysSettingService sysSettingService; + + private volatile boolean cachedIsMock = true; + private volatile long lastMockCheckTime = 0; + private static final long MOCK_CHECK_INTERVAL_MS = 30000; + + @RequestMapping("/api/daylight/proxy/**") + public ResponseEntity invokeDaylightApiProxy(HttpServletRequest request,HttpServletResponse response) throws Exception { + String apiUrl = daylightApiSetting.getServerUrl() + request.getRequestURI().replace("/api/daylight/proxy", ""); + HttpHeaders headers = this.extractHeaders(request); + TokenResponse daylightApiToken = daylightTokenService.getApiAccessToken(); + if(daylightApiToken==null || StringUtils.isBlank(daylightApiToken.getAccessToken())){ + throw new Exception("Invoke daylight token failed"); + } + headers.set("authorization","Bearer "+daylightApiToken.getAccessToken()); + return webProxyService.invokeRequest( + WebProxyService.DAYLIGHT_API, + HttpMethod.valueOf(request.getMethod()), + apiUrl, + request.getQueryString(), + headers, + getRequestBody(request), + Duration.ofSeconds(daylightApiSetting.getApiTimeout()), + httpErrorHandler + ).block(); + } + @RequestMapping("/api/esb/proxy/**") + public ResponseEntity invokeEsbApiProxy(HttpServletRequest request) throws Exception { + logger.info("invokeEsbApiProxy start, method={}, uri={}, query={}", + request.getMethod(), request.getRequestURI(), request.getQueryString()); + String apiUrl = esbApiSetting.getServerUrl() + request.getRequestURI().replace("/api/esb/proxy", ""); + logger.info("invokeEsbApiProxy target apiUrl={}", apiUrl); + HttpHeaders headers = this.extractHeaders(request); + + if (isMockFromDb()) { + return webProxyService.invokeMockRequest( + WebProxyService.ESB_API, + HttpMethod.valueOf(request.getMethod()), + apiUrl, + request.getQueryString(), + extractHeaders(request), + getRequestBody(request), + Duration.ofSeconds(esbApiSetting.getApiRequestTimeout()), + httpErrorHandler + ).block(); + } + + EsbToken esbToken = esbTokenService.getApiAccessToken(); + if(esbToken==null || StringUtils.isBlank(esbToken.getAccessToken())){ + logger.error("invokeEsbApiProxy failed, ESB token is null or blank, uri={}", request.getRequestURI()); + throw new Exception("Invoke ESB token failed"); + } + headers.set("authorization", esbToken.getAccessToken()); + + ResponseEntity response = webProxyService.invokeRequest( + WebProxyService.ESB_API, + HttpMethod.valueOf(request.getMethod()), + apiUrl, + request.getQueryString(), + extractHeaders(request), + getRequestBody(request), + Duration.ofSeconds(esbApiSetting.getApiRequestTimeout()), + httpErrorHandler + ).block(); + logger.info("invokeEsbApiProxy end, uri={}, status={}", + request.getRequestURI(), response == null ? null : response.getStatusCode()); + return response; + } + + @RequestMapping("/api/opera/proxy/**") + public ResponseEntity invokeOperaApiProxy(HttpServletRequest request) throws Exception { + String apiUrl = operaApiSetting.getServerUrl() + request.getRequestURI().replace("/api/opera/proxy", ""); + return webProxyService.invokeRequest( + WebProxyService.OPERA_API, + HttpMethod.valueOf(request.getMethod()), + apiUrl, + request.getQueryString(), + extractHeaders(request), + getRequestBody(request), + Duration.ofSeconds(operaApiSetting.getTimeout()), + soapProxyErrorHandler + ).block(); + } + + private HttpHeaders extractHeaders(HttpServletRequest request) { + HttpHeaders headers = new HttpHeaders(); + Collections.list(request.getHeaderNames()).forEach(name -> { + if (!name.equalsIgnoreCase("host") && + !name.equalsIgnoreCase("content-length") && + !name.equalsIgnoreCase("application-id") && + !name.equalsIgnoreCase("authorization") + ) { + headers.add(name, request.getHeader(name)); + } + }); + return headers; + } + + private byte[] getRequestBody(HttpServletRequest request) { + try { + return request.getInputStream().readAllBytes(); + } catch (IOException e) { + logger.error("Failed to read request body of request[{}]",request.getRequestURI(), e); + return new byte[0]; + } + } + + private boolean isMockFromDb() { + long now = System.currentTimeMillis(); + if (now - lastMockCheckTime > MOCK_CHECK_INTERVAL_MS) { + try { + var setting = sysSettingService.getSysSettingByKey("APP_MOCK"); + cachedIsMock = setting != null && "1".equals(setting.getSettingValue()); + } catch (Exception e) { + logger.error("Failed to read APP_MOCK from sys_settings, using cached value", e); + } + lastMockCheckTime = now; + } + return cachedIsMock; + } + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/exception/HttpProxyErrorHandler.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/exception/HttpProxyErrorHandler.java new file mode 100644 index 0000000..ec5f9a8 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/exception/HttpProxyErrorHandler.java @@ -0,0 +1,16 @@ +package com.mgm.mcrm.apigateway.proxy.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; + +public class HttpProxyErrorHandler implements ProxyErrorHandler { + @Override + public Mono> handleError(Throwable error) { + return Mono.just( + ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(("Forward proxy error: [" + error.getClass().getName() + "] " + error.getMessage()).getBytes(StandardCharsets.UTF_8))); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/exception/ProxyErrorHandler.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/exception/ProxyErrorHandler.java new file mode 100644 index 0000000..0c8494c --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/exception/ProxyErrorHandler.java @@ -0,0 +1,9 @@ +package com.mgm.mcrm.apigateway.proxy.exception; + +import org.springframework.http.ResponseEntity; +import reactor.core.publisher.Mono; + +@FunctionalInterface +public interface ProxyErrorHandler { + Mono> handleError(Throwable error); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/exception/SoapProxyErrorHandler.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/exception/SoapProxyErrorHandler.java new file mode 100644 index 0000000..3754b74 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/exception/SoapProxyErrorHandler.java @@ -0,0 +1,24 @@ +package com.mgm.mcrm.apigateway.proxy.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import reactor.core.publisher.Mono; + +import java.nio.charset.Charset; + +public class SoapProxyErrorHandler implements ProxyErrorHandler { + @Override + public Mono> handleError(Throwable error) { + String faultXml = String.format( + "" + + "soap:Server" + + "%s" + + "", + error.getMessage() + ); + + return Mono.just( + ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(faultXml.getBytes(Charset.forName("GBK")))); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/services/WebProxyService.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/services/WebProxyService.java new file mode 100644 index 0000000..a83da35 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/services/WebProxyService.java @@ -0,0 +1,167 @@ +package com.mgm.mcrm.apigateway.proxy.services; + +import com.mgm.mcrm.apigateway.common.utils.LogUtil; +import com.mgm.mcrm.apigateway.common.utils.mockup.HttpUtilMockup; +import com.mgm.mcrm.apigateway.proxy.exception.HttpProxyErrorHandler; +import com.mgm.mcrm.apigateway.proxy.exception.ProxyErrorHandler; +import lombok.RequiredArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; + +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.WebClient; + +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; +import reactor.core.publisher.Mono; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.concurrent.ForkJoinPool; + +@RequiredArgsConstructor +@Service +public class WebProxyService { + public final static String DAYLIGHT_API="DAYLIGHT_API"; + public final static String ESB_API="ESB_API"; + public final static String OPERA_API="OPERA_API"; + private final WebClient daylightWebClient; + private final WebClient esbWebClient; + private final WebClient operaWebClient; + private final Logger logger = LoggerFactory.getLogger(WebProxyService.class); + + public Mono> invokeRequest( + String apiType, + HttpMethod method, + String apiUrl, + String requestQuery, + HttpHeaders headers, + Object body, + Duration timeout, + ProxyErrorHandler errorHandler) throws Exception { + + WebClient targetWebClient = null; + + if(DAYLIGHT_API.equalsIgnoreCase(apiType)){ + targetWebClient=daylightWebClient; + }else if(ESB_API.equalsIgnoreCase(apiType)){ + targetWebClient=esbWebClient; + }else if(OPERA_API.equalsIgnoreCase(apiType)){ + targetWebClient=operaWebClient; + }else{ + throw new Exception("Unknown request API type of "+apiType); + } + + return targetWebClient.method(method) + .uri(uriBuilder -> this.buildRequestUri(apiUrl,requestQuery) + ) + .headers(httpHeaders -> filterAndSetHeaders(httpHeaders, headers)) + .bodyValue(body) + .exchangeToMono(response -> + response.bodyToMono(byte[].class) + .defaultIfEmpty(new byte[0]) + .map(responseBody ->{ + HttpHeaders responseHeaders = new HttpHeaders(); + this.filterIgnoreResponseHeaders(responseHeaders,response.headers().asHttpHeaders()); + return ResponseEntity.status(response.statusCode()) + .headers(extractHeaders(response)) + .body(responseBody); + }) + ) + .doOnError(e -> logger.error("Forward request failed: [{}] {}", e.getClass().getName(), e.getMessage(), e)) + .onErrorResume(errorHandler::handleError + ).timeout(timeout); + } + + private HttpHeaders extractHeaders(ClientResponse response) { + HttpHeaders headers = new HttpHeaders(); + response.headers().asHttpHeaders().forEach((name, values) -> { + if (!name.equalsIgnoreCase("transfer-encoding")) { + headers.addAll(name, values); + } + }); + return headers; + } + + private void filterAndSetHeaders(HttpHeaders targetHeaders, HttpHeaders sourceHeaders) { + sourceHeaders.forEach((key, values) -> { + if (!isIgnoreRequestHeader(key)) { + targetHeaders.addAll(key, values); + } + }); + } + + private boolean isIgnoreRequestHeader(String headerName) { + String lowerHeader = headerName.toLowerCase(); + return lowerHeader.equals("host") || + lowerHeader.equals("content-length") || + lowerHeader.equals("transfer-encoding") || + lowerHeader.equals("connection"); + } + + private void filterIgnoreResponseHeaders(HttpHeaders targetHeaders, HttpHeaders sourceHeaders) { + sourceHeaders.forEach((key, values) -> { + if (!isIgnoreResponseHeader(key)) { + targetHeaders.addAll(key, values); + } + }); + } + + private boolean isIgnoreResponseHeader(String headerName) { + String lowerHeader = headerName.toLowerCase(); + return lowerHeader.equals("transfer-encoding") || + lowerHeader.equals("content-encoding"); + } + + private URI buildRequestUri(String apiUrl, String requestQuery) { + UriComponents components = UriComponentsBuilder.fromUriString(apiUrl) + .replaceQuery(requestQuery) + .build(false); + return components.toUri(); + } + + public Mono> invokeMockRequest( + String apiType, + HttpMethod method, + String apiUrl, + String requestQuery, + HttpHeaders headers, + Object body, + Duration timeout, + ProxyErrorHandler errorHandler) throws Exception { + + String fullUrl = apiUrl; + if (requestQuery != null && !requestQuery.isEmpty()) { + fullUrl = apiUrl + "?" + requestQuery; + } + + String requestContent = ""; + if (body != null) { + if (body instanceof byte[]) { + requestContent = new String((byte[]) body, StandardCharsets.UTF_8); + } else { + requestContent = body.toString(); + } + } + + String mockResponse = HttpUtilMockup.processMockRequest(fullUrl, requestContent); + if (mockResponse == null) { + mockResponse = ""; + } + + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.setContentType(MediaType.APPLICATION_JSON); + + ResponseEntity responseEntity = ResponseEntity.status(200) + .headers(responseHeaders) + .body(mockResponse.getBytes(StandardCharsets.UTF_8)); + + return Mono.just(responseEntity); + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/reservation/models/Bookings.java b/src/main/java/com/mgm/mcrm/apigateway/reservation/models/Bookings.java new file mode 100644 index 0000000..23ca4d4 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/reservation/models/Bookings.java @@ -0,0 +1,219 @@ +package com.mgm.mcrm.apigateway.reservation.models; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.*; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name="bookings") +public class Bookings implements Serializable { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name="revision_of") + private Long revisionOf; + @Column(name="confirmation_no") + private String confirmationNo; + @Column(name="resv_id") + private String resvId; + @Column(name="last_opera_status") + private String lastOperaStatus; + @Column(name="player_id") + private String playerId; + @Column(name="member_title") + private String memberTitle; + @Column(name="member_first_name") + private String memberFirstName; + @Column(name="member_last_name") + private String memberLastName; + @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") + @Column(name="checkin_date") + private LocalDateTime checkinDate; + @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") + @Column(name="checkout_date") + private LocalDateTime checkoutDate; + @Column(name="property_id") + private Long propertyId; + @Column(name="room_type_id") + private Long roomTypeId; + @Column(name="dl_profile_id") + private String dlProfileId; + @Column(name="notify_language") + private String notifyLanguage; + @Column(name="payment_amount") + private Double paymentAmount; + @Column(name="opera_total") + private Double operaTotal; + @Column(name="comment") + private String comment; + @Column(name="dl_block_id") + private String dlBlockId; + @Column(name="rate_plan_code") + private String ratePlanCode; + @Column(name="chain_code") + private String chainCode; + @Column(name="hotel_code") + private String hotelCode; + @Column(name="room_type_code") + private String roomTypeCode; + @Column(name="promotion_code") + private String promotionCode; + @Column(name="dl_guarantee_type_id") + private String dlGuaranteeTypeId; + @Column(name="payment_type") + private String paymentType; + @Column(name="market_code") + private String marketCode; + @Column(name="dl_source_id") + private String dlSourceId; + @Column(name="cancelled_by") + private String cancelledBy; + @Column(name="cancelled_at") + private LocalDateTime cancelledAt; + @Column(name="section_id") + private Long sectionId; + @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") + @Column(name="created_at") + private LocalDateTime createdAt; + @Column(name="created_by") + private String createdBy; + @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") + @Column(name="updated_at") + private LocalDateTime updatedAt; + @Column(name="updated_by") + private String updatedBy; + @Column(name="approve_status") + private String approveStatus; + @Column(name="remark") + private String remark; + @Column(name="book_type") + private String bookType; + @Column(name="block") + private String block; + @Column(name="num_of_night") + private Integer numOfNight; + @Column(name="section_name") + private String sectionName; + @Column(name="booker") + private String booker; + @Column(name="msc_remark") + private String mscRemark; + @Column(name="approval_remark") + private String approvalRemark; + @Column(name="room") + private String room; + @Column(name="sharer") + private String sharer; + @Column(name="feature_code") + private String featureCode; + @Column(name="item_code") + private String itemCode; + @Column(name="bed_type_master_group_id") + private Integer bedTypeMasterGroupId; + @Column(name="bed_type_name") + private String bedTypeName; + @Column(name="created_by_name") + private String createdByName; + @Column(name="room_id") + private Integer roomId; + @Column(name = "source_code") + private String sourceCode; + @Column(name="block_id") + private Integer blockId; + @Column(name="try_time") + private Integer tryTime; + @Column(name="error_message") + private String errorMessage; + @Column(name="failed_at") + private LocalDateTime failedAt; + @Column(name="approved_by") + private String approvedBy; + @Column(name="approved_at") + private LocalDateTime approvedAt; + @Column(name="member") + private String member; + @Column(name="booking_type_id") + private Integer bookingTypeId; + @Column(name="group_id") + private String groupId; + @Column(name="date_list") + private String dateList; + @Column(name="failed_room_type") + private String failedRoomType; + @Column(name="process_status") + private Integer processStatus; + @Column(name="waitlist_id") + private Long waitlistId; + @Column(name="segment_id") + private Long segmentId; + @Column(name="other_room_status_id") + private Long otherRoomStatusId; + @Column(name="other_follow_up_status_id") + private Long otherFollowUpStatusId; + @Column(name="approved_id") + private Long approvedId; + @Column(name="requested_co_date") + private LocalDateTime requestedCoDate; + @Column(name="eta") + private String eta; + @Column(name="change_checkin_date") + private String changeCheckinDate; + @Column(name="change_checkout_date") + private String changeCheckoutDate; + @Column(name="dl_channel_id") + private String dlChannelId; + @Column(name="dl_account_id") + private String dlAccountId; + @Column(name="dl_reservation_id") + private String dlReservationId; + @Column(name="opera_profile_id") + private Long operaProfileId; + + @Column(name = "category") + private String category; + + @Column(name = "dl_purchase_element_id") + private String dlPurchaseElementId; + + @Column(name = "misc_charge") + private String miscCharge; + + @Column(name = "guarantee_type") + private String guaranteeType; + + @Column(name = "opera_comment") + private String operaComment; + + @Column(name = "opera_comment_id") + private Long operaCommentId; + + @Column(name = "dl_external_no") + private String dlExternalNo; + + @Transient + private String dlPropertyId; + @Transient + private String dlRoomTypeId; + //-- For export + @Transient + private String approveStatusName; + @Transient + private String bookTypeName; + @Transient + private int numOfRooms=1; + @Transient + private String otherRoomStatusName; + @Transient + private String otherFollowUpStatusName; + + @Transient + private String dlMarketSegmentId; + + @Transient + private String dlRatePlanId; +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/reservation/models/BookingsView.java b/src/main/java/com/mgm/mcrm/apigateway/reservation/models/BookingsView.java new file mode 100644 index 0000000..65fa371 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/reservation/models/BookingsView.java @@ -0,0 +1,263 @@ +package com.mgm.mcrm.apigateway.reservation.models; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.persistence.*; +import lombok.Data; +import org.hibernate.annotations.Immutable; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +@Data +@Entity +@Table(name="view_booking") +@Immutable +@NamedEntityGraphs({ + @NamedEntityGraph( + name = "graph.BookingsView.fulldetails", + attributeNodes = { + + } + ) +}) +public class BookingsView implements Serializable { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name="revision_of") + private Long revisionOf; + @Column(name="confirmation_no") + private String confirmationNo; + @Column(name="resv_id") + private String resvId; + @Column(name="last_opera_status") + private String lastOperaStatus; + @Column(name="player_id") + private String playerId; + @Column(name="member_title") + private String memberTitle; + @Column(name="member_first_name") + private String memberFirstName; + @Column(name="member_last_name") + private String memberLastName; + @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") + @Column(name="checkin_date") + private Date checkinDate; + @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") + @Column(name="checkout_date") + private Date checkoutDate; + @Column(name="property_id") + private Long propertyId; + @Column(name="room_type_id") + private Long roomTypeId; + @Column(name="dl_profile_id") + private Long dlProfileId; + @Column(name="notify_language") + private String notifyLanguage; + @Column(name="payment_amount") + private Double paymentAmount; + @Column(name="opera_total") + private Double operaTotal; + @Column(name="comment") + private String comment; + @Column(name="rate_plan_code") + private String ratePlanCode; + @Column(name="chain_code") + private String chainCode; + @Column(name="hotel_code") + private String hotelCode; + @Column(name="room_type_code") + private String roomTypeCode; + @Column(name="promotion_code") + private String promotionCode; + @Column(name="dl_guarantee_type_id") + private String dlGuaranteeTypeId; + @Column(name="payment_type") + private String paymentType; + @Column(name="market_code") + private String marketCode; + @Column(name="dl_source_id") + private String dlSourceId; + @Column(name="cancelled_by") + private String cancelledBy; + @Column(name="cancelled_at") + private Date cancelledAt; + @Column(name="section_id") + private Long sectionId; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + @Column(name="created_at") + private Date createdAt; + @Column(name="created_by") + private String createdBy; + @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") + @Column(name="updated_at") + private Date updatedAt; + @Column(name="updated_by") + private String updatedBy; + @Column(name="approve_status") + private String approveStatus; + @Column(name="remark") + private String remark; + @Column(name="book_type") + private String bookType; + @Column(name="dl_block_id") + private String dlBlockId; + @Column(name="num_of_night") + private Integer numOfNight; + @Column(name="section_name") + private String sectionName; + @Column(name="booker") + private String booker; + @Column(name="msc_remark") + private String mscRemark; + @Column(name="approval_remark") + private String approvalRemark; + @Column(name="room") + private String room; + @Column(name="sharer") + private String sharer; + @Column(name="feature_code") + private String featureCode; + @Column(name="item_code") + private String itemCode; + @Column(name="group_id") + private String groupId; + @Column(name="property_name") + private String propertyName; + @Column(name="block_id") + private Long blockId; + @Column(name="room_id") + private Long roomId; + @Column(name="property_category") + private String propertyCategory; + @Column(name="bed_type_master_group_id") + private Integer bedTypeMasterGroupId; + @Column(name="bed_type_name") + private String bedTypeName; + @Column(name="created_by_name") + private String createdByName; + @Column(name="date_list") + private String dateLists; + @Column(name="room_cn") + private String roomCN; + @Column(name="error_message") + private String errorMessage; + @Column(name="try_time") + private Integer tryTime; + @JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8") + @Column(name="failed_at") + private Date failedAt; + @Column(name="booking_type_name") + private String bookingTypeName; + @Column(name="room_category") + private String roomCategory; + @Column(name="for_booking") + private Integer roomForBooking; + @Column(name="for_waitlist") + private Integer roomForWaitlist; + @Column(name="segment_id") + private Long segmentId; + @Column(name="requested_co_date") + private Date requestedCoDate; + @Column(name="tier") + private String tier; + @Column(name="gaming_id") + private Long gamingId; + @Column(name="overlap_booking") + private String overlapBooking; + @Column(name="ma_booking") + private String maBooking; + @Column(name="r3_theo_win") + private String r3TheoWin; + @Column(name="r3_tpd") + private String r3Tpd; + @Column(name="r3_actual_win") + private String r3ActualWin; + @Column(name="r6_theo_win") + private String r6TheoWin; + @Column(name="r6_tpd") + private String r6Tpd; + @Column(name="r6_actual_win") + private String r6ActualWin; + @Column(name="r12_theo_win") + private String r12TheoWin; + @Column(name="r12_tpd") + private String r12Tpd; + @Column(name="r12_actual_win") + private String r12ActualWin; + @Column(name="final_segment") + private String finalSegment; + @Column(name="trip_theo_profit") + private String tripTheoProfit; + @Column(name="daily_theo_profit") + private String dailyTheoProfit; + @Column(name="player_type") + private String playerType; + @Column(name="ranking_by_player") + private String rankingByPlayer; + @Column(name="booker_team") + private String bookerTeam; + @Column(name="current_trip_theo_win_amount") + private String currentTripTheoWinAmount; + @Column(name="last_2day_theo_win_amount") + private String last2dayTheoWinAmount; + @Column(name="approved_id") + private Long approvedId; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8") + @Column(name="approved_at") + private Date approvedAt; + @Column(name="approved_by") + private String approvedBy; + @Column(name="property_email_name") + private String propertyEmailName; + @Column(name="room_email_name") + private String roomEmailName; + @Column(name="eta") + private String eta; + @Column(name="change_checkin_date") + private String changeCheckinDate; + @Column(name="change_checkout_date") + private String changeCheckoutDate; + @Column(name="dl_channel_id") + private String dlChannelId; + @Column(name="dl_account_id") + private String dlAccountId; + @Column(name="dl_reservation_id") + private String dlReservationId; + + + @Transient + private String status; + @Transient + private Long pendingRevisionId; + @Transient + private String specialName; + @Transient + private List dateList; + @Transient + private JSONArray sharerList; + @Transient + private String memberTier; + @Transient + private String sharerName; + @Transient + private String expDate; + @Transient + private String profieExpDate; + @Transient + private String roomNo; + @Transient + private Boolean isShare; + @Transient + private Boolean isQueue; + @Transient + private String dlPropertyId; + @Transient + private String dlRoomTypeId; + + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/reservation/repositories/BookingDao.java b/src/main/java/com/mgm/mcrm/apigateway/reservation/repositories/BookingDao.java new file mode 100644 index 0000000..1f3e486 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/reservation/repositories/BookingDao.java @@ -0,0 +1,16 @@ +package com.mgm.mcrm.apigateway.reservation.repositories; + +import com.mgm.mcrm.apigateway.reservation.models.Bookings; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + + +public interface BookingDao extends JpaRepository { + + List findByConfirmationNo(String confirmationNo); + List findByDlReservationId(String dlReservationId); + + Bookings findFirstByDlProfileIdAndPlayerIdIsNotNull(String dlProfileId); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java b/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java new file mode 100644 index 0000000..5f63aab --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java @@ -0,0 +1,380 @@ +package com.mgm.mcrm.apigateway.reservation.services; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; + +import com.mgm.mcrm.apigateway.basesetting.mapping.models.CfgBlockCodes; +import com.mgm.mcrm.apigateway.basesetting.mapping.models.CfgDaylightView; +import com.mgm.mcrm.apigateway.basesetting.mapping.models.CfgMarketCodes; +import com.mgm.mcrm.apigateway.basesetting.mapping.models.CfgRateCodes; +import com.mgm.mcrm.apigateway.basesetting.mapping.repositories.BlockCodeDao; +import com.mgm.mcrm.apigateway.basesetting.mapping.repositories.DaylightViewDao; +import com.mgm.mcrm.apigateway.basesetting.mapping.repositories.MarketCodeDao; +import com.mgm.mcrm.apigateway.basesetting.mapping.repositories.RateCodeDao; +import com.mgm.mcrm.apigateway.basesetting.property.models.CfgProperties; +import com.mgm.mcrm.apigateway.basesetting.property.repositories.PropertyDao; +import com.mgm.mcrm.apigateway.basesetting.room.models.CfgRoomTypes; +import com.mgm.mcrm.apigateway.basesetting.room.models.CfgRooms; +import com.mgm.mcrm.apigateway.basesetting.room.models.CfgRoomsRoomTypes; +import com.mgm.mcrm.apigateway.basesetting.room.repositories.RoomDao; +import com.mgm.mcrm.apigateway.basesetting.room.repositories.RoomRoomTypeDao; +import com.mgm.mcrm.apigateway.basesetting.room.repositories.RoomTypeDao; +import com.mgm.mcrm.apigateway.common.dtos.ServiceResult; +import com.mgm.mcrm.apigateway.common.utils.DateTimeUtil; +import com.mgm.mcrm.apigateway.common.utils.LogUtil; +import com.mgm.mcrm.apigateway.esb.common.dtos.mgmapi.PlayerProfileApiResult; +import com.mgm.mcrm.apigateway.esb.common.dtos.mgmapi.UserInfoApiRecord; +import com.mgm.mcrm.apigateway.esb.player.services.MgmUserService; +import com.mgm.mcrm.apigateway.player.services.PlayerService; +import com.mgm.mcrm.apigateway.reservation.models.Bookings; +import com.mgm.mcrm.apigateway.reservation.repositories.BookingDao; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + + +@Service +public class BookingService { + + @Resource + private BookingDao bookingDao; + @Resource + private RoomTypeDao roomTypeDao; + @Resource + private RoomRoomTypeDao roomRoomTypeDao; + @Resource + private RoomDao roomDao; + @Resource + private MarketCodeDao marketCodeDao; + @Resource + private RateCodeDao rateCodeDao; + @Resource + private DaylightViewDao daylightViewDao; + @Resource + private PropertyDao propertyDao; + @Resource + private PlayerService playerService; + @Resource + private MgmUserService mgmUserService; + @Resource + private BlockCodeDao blockCodeDao; + + public void processDaylightUpdateBooking(Bookings daylightBooking) throws Exception { + if(daylightBooking!=null){ + String dlReservationId=daylightBooking.getDlReservationId(); + String confirmNo = daylightBooking.getConfirmationNo(); + if(StringUtils.isNotBlank(confirmNo) + && StringUtils.isNotBlank(dlReservationId) + ){ + List dbBookings = bookingDao.findByDlReservationId(dlReservationId); + if (CollectionUtils.isEmpty(dbBookings)) { + dbBookings = bookingDao.findByConfirmationNo(confirmNo); + } + + if (CollectionUtils.isEmpty(dbBookings)) { + TimeUnit.SECONDS.sleep(5); + dbBookings = bookingDao.findByDlReservationId(dlReservationId); + + if (CollectionUtils.isEmpty(dbBookings)) { + dbBookings = bookingDao.findByConfirmationNo(confirmNo); + } + } + + Bookings dbBooking=null; + if(dbBookings!=null && !dbBookings.isEmpty()) { + for(Bookings booking:dbBookings){ + if("N".equalsIgnoreCase(booking.getApproveStatus())) { + dbBooking = booking; + break; + } + } + } + if(dbBooking!=null){ + this.mergeDaylightToDbBooking(daylightBooking,dbBooking); + }else{ + dbBooking = this.createBookingFromDaylight(daylightBooking); + } + bookingDao.save(dbBooking); + } + } + } + + public Bookings findOneByDlProfileId(String dlProfileId) { + return bookingDao.findFirstByDlProfileIdAndPlayerIdIsNotNull(dlProfileId); + } + + // 第一条是daylight返回过来的booking,第二条是本地数据库的booking + public void mergeDaylightToDbBooking(Bookings daylightBooking, Bookings updateBooking) throws Exception { + String nights = DateTimeUtil.getDiffDate(daylightBooking.getCheckinDate(), daylightBooking.getCheckoutDate(), "day"); + daylightBooking.setNumOfNight(Integer.valueOf(nights)); + if(!"PN".equals(updateBooking.getApproveStatus())) { + updateBooking.setCheckinDate(daylightBooking.getCheckinDate()); + updateBooking.setCheckoutDate(daylightBooking.getCheckoutDate()); + updateBooking.setNumOfNight(daylightBooking.getNumOfNight()); + } + updateBooking.setConfirmationNo(daylightBooking.getConfirmationNo()); + updateBooking.setHotelCode(daylightBooking.getHotelCode()); + updateBooking.setDlProfileId(daylightBooking.getDlProfileId()); + updateBooking.setDlSourceId(daylightBooking.getDlSourceId()); + updateBooking.setDlGuaranteeTypeId(daylightBooking.getDlGuaranteeTypeId()); + updateBooking.setDlChannelId(daylightBooking.getDlChannelId()); + updateBooking.setDlAccountId(daylightBooking.getDlAccountId()); + updateBooking.setDlBlockId(daylightBooking.getDlBlockId()); + updateBooking.setDlExternalNo(daylightBooking.getDlExternalNo()); +// updateBooking.setGroupId(daylightBooking.getGroupId()); + updateBooking.setDlReservationId(daylightBooking.getDlReservationId()); + // 通过 dlRoomTypeId 查找 room type(与 createBookingFromDaylight 流程保持一致) + CfgRoomTypes daylightRoomType = roomTypeDao.findByDlRoomTypeId(daylightBooking.getDlRoomTypeId()); + if(daylightRoomType!=null) { + updateBooking.setRoomTypeId(daylightRoomType.getId()); + updateBooking.setRoomTypeCode(daylightRoomType.getCode()); + List roomroomType = roomRoomTypeDao.findByRoomTypeId(daylightRoomType.getId()); + if(!roomroomType.isEmpty()) { + for(CfgRoomsRoomTypes r:roomroomType) { + CfgRooms room = roomDao.findById(r.getRoomId()).orElse(null); + if(room!=null) { + if(room.getPropertyId().equals(updateBooking.getPropertyId())) { + //updateBooking.setRoom(room.getName()); + updateBooking.setRoomId(Math.toIntExact(room.getId())); + break; + } + } + } + } + } + //get Booking Type + List operaMarketCode = marketCodeDao.findByCode(daylightBooking.getMarketCode()); + List operRateCode = rateCodeDao.findByCode(daylightBooking.getRatePlanCode()); + if(!operaMarketCode.isEmpty() && !operRateCode.isEmpty()) { + Long operaMarketCodeId = operaMarketCode.getFirst().getId(); + Long operaRateCodeId = operRateCode.getFirst().getId(); + List bookingTypeOperas = daylightViewDao.findByMarketCodeIdAndRateCodeId(operaMarketCodeId,operaRateCodeId); + if(!bookingTypeOperas.isEmpty()) { + Long operaBookingTypeId = bookingTypeOperas.getFirst().getBookingTypeId(); + updateBooking.setBookingTypeId(Math.toIntExact(operaBookingTypeId)); + } + } + + if("CANCELED".equals(daylightBooking.getLastOperaStatus())) { + updateBooking.setApproveStatus("C"); + daylightBooking.setApproveStatus("C"); + }else { + if("C".equals(updateBooking.getApproveStatus())||daylightBooking.getConfirmationNo()!=null) { + updateBooking.setApproveStatus("N"); + daylightBooking.setApproveStatus("N"); + } + } + updateBooking.setLastOperaStatus(daylightBooking.getLastOperaStatus()); + + //get player info + if(updateBooking.getMember()==null||StringUtils.isEmpty(updateBooking.getMember())) { + if(!StringUtils.isEmpty(updateBooking.getPlayerId())) { + ServiceResult serviceResult = playerService.findWebPlayerProfileByPlayerId(updateBooking.getPlayerId()); + if(serviceResult.isSuccess()) { + PlayerProfileApiResult profile = (PlayerProfileApiResult) serviceResult.getReturnData(); + updateBooking.setMemberLastName(profile.getName()); + updateBooking.setMemberTitle(profile.getSalutation()); + updateBooking.setMember(JSONObject.toJSONString(profile)); + } + } + } + //get user info + boolean getUser = false; + String booker = StringUtils.trim(updateBooking.getBooker()); + if(booker==null||StringUtils.isEmpty(booker)) getUser=true; + if(daylightBooking.getCreatedBy()!=null && !daylightBooking.getCreatedBy().equals(updateBooking.getCreatedBy())) { + updateBooking.setCreatedBy(daylightBooking.getCreatedBy()); + getUser=true; + } + if(getUser) { + if(!StringUtils.isEmpty(updateBooking.getCreatedBy())) { + ServiceResult userInfoRes = mgmUserService.findUserInfoByUserId(updateBooking.getCreatedBy()); + if(userInfoRes.isSuccess()) { + UserInfoApiRecord userInfo = (UserInfoApiRecord)userInfoRes.getReturnData(); + updateBooking.setBooker(userInfo.getUserName()); + updateBooking.setCreatedByName(userInfo.getUserName()); + } + } + } + updateBooking.setUpdatedAt(LocalDateTime.now()); + } + + public Bookings createBookingFromDaylight(Bookings daylightBooking) throws Exception { + String dlReservationId = daylightBooking.getDlReservationId(); + + List dbBookings = bookingDao.findByDlReservationId(dlReservationId); + + if (!dbBookings.isEmpty()) { + Bookings dbBooking = null; + for (Bookings booking : dbBookings) { + if ("N".equalsIgnoreCase(booking.getApproveStatus())) { + dbBooking = booking; + break; + } + } + + if (dbBooking != null) { + mergeDaylightToDbBooking(daylightBooking, dbBooking); + return dbBooking; + } + } + + Bookings dbBooking = new Bookings(); + CfgProperties daylightProperty = propertyDao.findByDlPropertyId(daylightBooking.getDlPropertyId()); + CfgRoomTypes daylightRoomType =roomTypeDao.findByDlRoomTypeId(daylightBooking.getDlRoomTypeId()); + CfgRooms daylightRoom = new CfgRooms(); + //set room type + if(daylightRoomType!=null) { + List roomroomTypes = roomRoomTypeDao.findByRoomTypeId(daylightRoomType.getId()); + if(!roomroomTypes.isEmpty()) { + for(CfgRoomsRoomTypes r:roomroomTypes) { + CfgRooms room = roomDao.findById(r.getRoomId()).orElse(null); + if(room!=null) { + if(room.getPropertyId().equals(daylightProperty.getId())) { + daylightRoom = room; + break; + } + } + } + } + + } + String operaMemberTitle = ""; + String operaMemberFirstName = ""; + String operaMemberLastName = ""; + + CfgBlockCodes byDlBlockId = blockCodeDao.findByDlBlockId(daylightBooking.getDlBlockId()); + if (byDlBlockId != null) { + dbBooking.setBlockId(Math.toIntExact(byDlBlockId.getId())); + } + + dbBooking.setCheckinDate(daylightBooking.getCheckinDate()); + dbBooking.setCheckoutDate(daylightBooking.getCheckoutDate()); + dbBooking.setPropertyId(daylightProperty.getId()); + dbBooking.setNotifyLanguage("en"); + //dbBooking.setRoom(daylightRoom.getId()!=null?daylightRoom.getName():""); + dbBooking.setRoomId(daylightRoom.getId()!=null?Integer.parseInt(String.valueOf(daylightRoom.getId())):-1); + dbBooking.setRoomTypeId(daylightRoomType!=null?daylightRoomType.getId():-1); + dbBooking.setRatePlanCode(daylightBooking.getRatePlanCode()); + dbBooking.setChainCode(daylightProperty.getCode()); + dbBooking.setHotelCode(daylightBooking.getHotelCode()); + + dbBooking.setRoomTypeCode(daylightRoomType!=null?daylightRoomType.getCode():null); + dbBooking.setRoomId(daylightRoom.getId()!=null?daylightRoom.getId().intValue():-1); + dbBooking.setCreatedBy(daylightBooking.getCreatedBy()); + dbBooking.setCreatedByName(daylightBooking.getCreatedBy()); + + dbBooking.setMemberTitle(operaMemberTitle); + dbBooking.setMemberFirstName(operaMemberFirstName); + dbBooking.setMemberLastName(operaMemberLastName); + + dbBooking.setMarketCode(daylightBooking.getMarketCode()); + dbBooking.setDlSourceId(daylightBooking.getDlSourceId()); + dbBooking.setDlProfileId(daylightBooking.getDlProfileId()); + dbBooking.setDlGuaranteeTypeId(daylightBooking.getDlGuaranteeTypeId()); + dbBooking.setDlChannelId(daylightBooking.getDlChannelId()); + dbBooking.setDlAccountId(daylightBooking.getDlAccountId()); + dbBooking.setDlBlockId(daylightBooking.getDlBlockId()); + dbBooking.setDlExternalNo(daylightBooking.getDlExternalNo()); + dbBooking.setGroupId(daylightBooking.getGroupId()); + + dbBooking.setPaymentAmount(daylightBooking.getPaymentAmount()); +// book.setGuaranteeType("CGTD"); + dbBooking.setPaymentType(null); +// book.setOperaProfileId(operaBooking.getOperaProfileId()); + dbBooking.setApproveStatus("N"); + dbBooking.setCategory("DAYLIGHT"); + + dbBooking.setCreatedAt(DateTimeUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss")); + dbBooking.setUpdatedAt(DateTimeUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss")); +// book.setTeamId(objTeam.getId()); + dbBooking.setLastOperaStatus(daylightBooking.getLastOperaStatus()); + dbBooking.setConfirmationNo(daylightBooking.getConfirmationNo()); + dbBooking.setResvId(daylightBooking.getResvId()); + dbBooking.setSharer(new JSONArray().toJSONString()); + dbBooking.setBookType("0"); + dbBooking.setDlReservationId(daylightBooking.getDlReservationId()); + + //get player info + if(!StringUtils.isEmpty(dbBooking.getPlayerId())) { + ServiceResult serviceResult = playerService.findWebPlayerProfileByPlayerId(dbBooking.getPlayerId()); + if(serviceResult.isSuccess()) { + PlayerProfileApiResult profile = (PlayerProfileApiResult) serviceResult.getReturnData(); + dbBooking.setMemberLastName(profile.getName()); + dbBooking.setMemberTitle(profile.getSalutation()); + dbBooking.setMember(JSONObject.toJSONString(profile)); + } + } + //get user info + // 这个判断不会走进去,因为createdBy必定是null,因为eventData没有这个字段 by Mauve 20260806 + if(!StringUtils.isEmpty(dbBooking.getCreatedBy())) { + ServiceResult userInfoRes = mgmUserService.findUserInfoByUserId(dbBooking.getCreatedBy()); + if(userInfoRes.isSuccess()) { + UserInfoApiRecord userInfo = (UserInfoApiRecord)userInfoRes.getReturnData(); + + dbBooking.setBooker(userInfo.getUserName()); + dbBooking.setCreatedByName(userInfo.getUserName()); + dbBooking.setSectionId(userInfo.getSectionId()); + + CfgMarketCodes byDlMarketSegmentId = + marketCodeDao.findByDlMarketSegmentId(daylightBooking.getDlMarketSegmentId()); + + LogUtil.info(BookingService.class, "sectionId=" + userInfo.getSectionId() + + ", marketCodeId=" + byDlMarketSegmentId.getId() + + ", propertyId=" + daylightProperty.getId() + + ", roomTypeId=" + daylightRoomType.getId()); + + Optional bySectionPropertyRoomTypeOptional = daylightViewDao.findBySectionMarketPropertyRoomType( + userInfo.getSectionId(), + byDlMarketSegmentId.getId(), + daylightProperty.getId(), + daylightRoomType.getId() + ); + + CfgDaylightView bySectionPropertyRoomType = bySectionPropertyRoomTypeOptional.orElse(null); + if (bySectionPropertyRoomType != null) { + dbBooking.setSectionId(bySectionPropertyRoomType.getBookingTypeId()); + } + + LogUtil.info(BookingService.class, "DaylightView=" + JSONObject.toJSONString(bySectionPropertyRoomType)); + } + } + + //get Booking Type + List operaMarketCode = marketCodeDao.findByCode(daylightBooking.getMarketCode()); + List operRateCode = rateCodeDao.findByCode(daylightBooking.getRatePlanCode()); + if(!operaMarketCode.isEmpty() && !operRateCode.isEmpty()) { + Long operaMarketCodeId = operaMarketCode.getFirst().getId(); + Long operaRateCodeId = operRateCode.getFirst().getId(); + List bookingTypeOperas = daylightViewDao.findByMarketCodeIdAndRateCodeId(operaMarketCodeId,operaRateCodeId); + if(!bookingTypeOperas.isEmpty()) { + Long operaBookingTypeId = bookingTypeOperas.getFirst().getBookingTypeId(); + dbBooking.setBookingTypeId(Integer.valueOf(String.valueOf(operaBookingTypeId))); + } + } + // build datelist + int numOfNight = Integer.parseInt(DateTimeUtil.getDiffDate(dbBooking.getCheckinDate(), dbBooking.getCheckoutDate(), "day")); + ArrayList dates = new ArrayList<>(); + ArrayList dateList = new ArrayList<>(); + JSONObject obj = new JSONObject(); + for(int j=0;j getOperaNameIDByPlayerID(String playerId){ + EntityManager entity = this.factory.createEntityManager(); + List lists = new ArrayList(); + try { + StoredProcedureQuery store = entity.createNamedStoredProcedureQuery("getOperaNameIDByPlayerID"); + store.setParameter("player_id", playerId); + lists = store.getResultList(); + entity.close(); + } catch (Exception e) { + entity.close(); + e.printStackTrace(); + } + return lists; + } + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/system/models/SysGroupPermission.java b/src/main/java/com/mgm/mcrm/apigateway/system/models/SysGroupPermission.java new file mode 100644 index 0000000..5ca9978 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/system/models/SysGroupPermission.java @@ -0,0 +1,23 @@ +package com.mgm.mcrm.apigateway.system.models; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@Entity +@Table(name="sys_group_permissions") +@IdClass(SysGroupPermissionKey.class) +public class SysGroupPermission { + //fields in table + @Id + @Column(name = "group_id") + private Long groupId; + @Id + @Column(name="permission_id") + private String permissionId; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/system/models/SysGroupPermissionKey.java b/src/main/java/com/mgm/mcrm/apigateway/system/models/SysGroupPermissionKey.java new file mode 100644 index 0000000..8623dc1 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/system/models/SysGroupPermissionKey.java @@ -0,0 +1,38 @@ +package com.mgm.mcrm.apigateway.system.models; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Objects; + +@Getter +@Setter +@NoArgsConstructor +public class SysGroupPermissionKey implements Serializable{ + + @Serial + private static final long serialVersionUID = 6060166117433738173L; + private Long groupId ; + private String permissionId; + @Override + public boolean equals(Object o) { + if(o instanceof SysGroupPermissionKey) + { + SysGroupPermissionKey key = (SysGroupPermissionKey)o ; + if(Objects.equals(this.groupId, key.getGroupId()) && Objects.equals(this.permissionId, key.getPermissionId())) + { + return true ; + } + } + return false ; + } + + @Override + public int hashCode() { + return 1; + } + +} \ No newline at end of file diff --git a/src/main/java/com/mgm/mcrm/apigateway/system/models/SysUserView.java b/src/main/java/com/mgm/mcrm/apigateway/system/models/SysUserView.java new file mode 100644 index 0000000..8161e7a --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/system/models/SysUserView.java @@ -0,0 +1,45 @@ +package com.mgm.mcrm.apigateway.system.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.util.Date; + +@Data +@Entity +@Table(name="view_sys_user_group") +public class SysUserView { + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(name="login_id") + private String loginId; + @Column(name="display_name") + private String displayName; + @Column(name="password") + private String password; + @Column(name="category") + private Integer category; + @Column(name="status") + private Integer status; + @Column(name="try_times") + private Integer tryTimes; + @Column(name="created_at") + private Date createdAt; + @Column(name="created_by") + private String createdBy; + @Column(name="updated_at") + private Date updatedAt; + @Column(name="updated_by") + private String updatedBy; + @Column(name="group_id") + private Long groupId; + @Column(name="group_name") + private String groupName; + @Column(name="default_property") + private String defaultProperty; + @Transient + private String token; + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/system/repositories/SysGroupPermissionDao.java b/src/main/java/com/mgm/mcrm/apigateway/system/repositories/SysGroupPermissionDao.java new file mode 100644 index 0000000..f40ea4e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/system/repositories/SysGroupPermissionDao.java @@ -0,0 +1,19 @@ +package com.mgm.mcrm.apigateway.system.repositories; + +import com.mgm.mcrm.apigateway.system.models.SysGroupPermission; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + + +public interface SysGroupPermissionDao extends JpaRepository { + List findAll(Specification spec,Sort sort); + List findAll(Specification spec); + + @Query(value="select * from sys_group_permissions where group_id=?1",nativeQuery=true) + List getAllByGroupId(long groupId); + +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/system/repositories/SysUserViewDao.java b/src/main/java/com/mgm/mcrm/apigateway/system/repositories/SysUserViewDao.java new file mode 100644 index 0000000..17dc6bd --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/system/repositories/SysUserViewDao.java @@ -0,0 +1,14 @@ +package com.mgm.mcrm.apigateway.system.repositories; + +import com.mgm.mcrm.apigateway.system.models.SysUserView; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + + +public interface SysUserViewDao extends JpaRepository { + SysUserView findById(long id); + + @Query(value="select * from view_sys_user_group where upper(login_id) =?1",nativeQuery=true) + SysUserView getByLoginId(String loginId); + +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..1a518b3 --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,140 @@ +spring: + datasource: + mgm: + driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver + jdbc-url: jdbc:sqlserver://47.238.46.81:1433;databasename=db_mgm_mcrm_daylight;encrypt=false;trustServerCertificate=false;hostNameInCertificate=*;loginTimeout=30 + username: sa + password: 666888@Welcomewilltech + continue-on-error: true + hikari: + minimum-idle: 10 + maximum-pool-size: 50 + idle-timeout: 600000 + connection-timeout: 600000 + max-lifetime: 1800000 + + opera: + driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver + jdbc-url: jdbc:sqlserver://47.238.46.81:1433;databasename=db_mgm_mcrm_daylight;encrypt=false;trustServerCertificate=false;hostNameInCertificate=*;loginTimeout=30 + username: sa + password: 666888@Welcomewilltech + continue-on-error: true + hikari: + minimum-idle: 10 + maximum-pool-size: 50 + idle-timeout: 600000 + connection-timeout: 600000 + max-lifetime: 1800000 + + http: + encoding: + force: true + charset: UTF-8 + enabled: true + messages: + basename: i18n/messages + jpa: + show-sql: false + properties: + hibernate: + default_schema: dbo + format_sql: true + + servlet: + multipart: + max-request-size: 10MB + max-file-size: 10MB + + devtools: + restart: + enabled: false + +#============== App Configuration =============== +mcrm: + proxy: + max-connection-pool: 100 + connect-timeout: 20 + read-timeout: 60 + api-config: + daylight: + proxy-setting: + host: + port: + auth: + username: + password: + ssl-bundle-name: + server-url: https://eu1.api.uat.development.abovecloud.io + request-timeout: 30 + token: + token-url: /connect/token + username: mcrm.mcrm@MGMUAT + password: Shiji@123 + grant-type: password + client-id: 3rd.willtech_technology + client-secret: HJIvAynCSCqvjEe8MC9K + request-timeout: 30 + default-expiry-second: 700 + security: + authorization-data: "[MCRM_APP/securItykeyOfCrmapp87895312],[MCRM_CMS/seCuritykeyOfCrmCms8wetw432]" + opera: + proxy-setting: + host: + port: + auth: + username: + password: + ssl-bundle-name: + server-url: + request-timeout: 30 + security: + authorization-data: "[MCRM_APP/securItykeyOfCrmapp87895312],[MCRM_CMS/seCuritykeyOfCrmCms8wetw432]" + esb: + proxy-setting: + host: + port: + auth: + username: + password: + ssl-bundle-name: + server-url: https://hca.mcmsapoc.com + request-timeout: 30 + token: + token-url: /RequestAccessToken + app-id: crmappid + app-secret: crmsecuritykey + request-timeout: 30 + default-expiry-second: 700 + security: + authorization-data: "[MCRM_APP/securItykeyOfCrmapp87895312],[MCRM_CMS/seCuritykeyOfCrmCms8wetw432]" + notification: + email-sender: + smtp-server: smtp.qq.com + smtp-port: 587 + smtp-auth: true + username: 201249946@qq.com + password: oheejklduyydbife + starttls: true + default-send-to: 201249946@qq.com + security: + authorization-data: "[MCRM_APP/securItykeyOfCrmapp87895312],[MCRM_CMS/seCuritykeyOfCrmCms8wetw432]" +server: + port: 9087 + referrer-policy: origin-when-cross-origin + tomcat: + uri-encoding: UTF-8 + +logging: + config: classpath:logback.xml + + +springdoc: + swagger-ui: + path: /swagger-ui.html + operations-sorter: alpha + tags-sorter: alpha + doc-expansion: none + api-docs: + path: /v3/api-docs + default-consumes-media-type: application/json + default-produces-media-type: application/json \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..caf4dfc --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,3 @@ +spring: + profiles: + active: dev \ No newline at end of file diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/i18n/messages_en_US.properties b/src/main/resources/i18n/messages_en_US.properties new file mode 100644 index 0000000..bb655c6 --- /dev/null +++ b/src/main/resources/i18n/messages_en_US.properties @@ -0,0 +1,26 @@ +gamecenter.title=Game List +gamecenter.welcome=Welcome, +gamecenter.will_text=CARD NO.: +gamecenter.button.back=BACK +gamecenter.gamechance=Game Chance(s): + +common.error_message.error_code_200=OK +common.error_message.error_code_401=Authorization failed +common.error_message.error_code_422=Parameter error +common.error_message.error_code_301=Game is unavailable +common.error_message.error_code_302=Game chance has been exhausted +common.error_message.error_code_303=Error result return +common.error_message.error_code_304=Data duplicated +common.error_message.error_code_305=Invalid request +common.error_message.error_code_306=Session timeout, please login and re-enter game +common.error_message.error_code_307=Reward exhausted, game is finished +common.error_message.error_code_701=Request error +common.error_message.error_code_308=Game Session Timeout +common.error_message.error_code_309=No Customer Tier +common.error_message.error_code_310=Game is finished, gamechance returned + +common.no_game_chance=Game chance has been exhausted +common.get_game_chance_error=Get game chance error +common.get_game_chance_request_error=Get game chance request error +common.no_game_available=No Game Available +common.game_session_timeout=Session timeout \ No newline at end of file diff --git a/src/main/resources/i18n/messages_zh_CN.properties b/src/main/resources/i18n/messages_zh_CN.properties new file mode 100644 index 0000000..198a24e --- /dev/null +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -0,0 +1,26 @@ +gamecenter.title=\u6e38\u620f\u4e2d\u5fc3 +gamecenter.welcome=\u6b22\u8fce, +gamecenter.will_text=\u5361\u53f7: +gamecenter.button.back=\u8fd4\u56de +gamecenter.gamechance=\u6e38\u620f\u673a\u4f1a: + +common.error_message.error_code_200=\u6210\u529f +common.error_message.error_code_401=\u8bf7\u6c42\u5b89\u5168\u9a8c\u8bc1\u5931\u8d25 +common.error_message.error_code_422=\u53c2\u6570\u9519\u8bef +common.error_message.error_code_301=\u6e38\u620f\u672a\u5f00\u653e +common.error_message.error_code_302=\u6e38\u620f\u6b21\u6570\u5df2\u7528\u5b8c +common.error_message.error_code_303=\u9519\u8bef\u7684\u8fd4\u56de\u7ed3\u679c +common.error_message.error_code_304=\u6570\u636e\u91cd\u590d +common.error_message.error_code_305=\u975e\u6cd5\u8bf7\u6c42 +common.error_message.error_code_306=\u6e38\u620f\u8d85\u65f6, \u8bf7\u91cd\u65b0\u8fdb\u5165 +common.error_message.error_code_307=\u5956\u9879\u5df2\u6d3e\u53d1\u5b8c\uff0c\u6e38\u620f\u5df2\u7ed3\u675f +common.error_message.error_code_701=\u8bf7\u6c42\u5931\u8d25 +common.error_message.error_code_308=\u6e38\u620f\u8d85\u65f6, \u8bf7\u91cd\u65b0\u8fdb\u5165 +common.error_message.error_code_309=\u7372\u53d6\u6703\u54e1\u7b49\u7d1a\u5931\u6557 +common.error_message.error_code_310=\u6e38\u620f\u5df2\u7ed3\u675f, \u4e0d\u4f1a\u6263\u9664\u6e38\u620f\u673a\u4f1a + +common.no_game_chance=\u6e38\u620f\u6b21\u6570\u5df2\u7528\u5b8c +common.get_game_chance_error=\u83b7\u53d6\u6e38\u620f\u6b21\u6570\u9519\u8bef +common.get_game_chance_request_error=\u83b7\u53d6\u6e38\u620f\u6b21\u6570\u8bf7\u6c42\u9519\u8bef +common.no_game_available=\u6ca1\u6709\u53ef\u53c2\u4e0e\u7684\u6e38\u620f +common.game_session_timeout=\u6e38\u620f\u8d85\u65f6 \ No newline at end of file diff --git a/src/main/resources/i18n/messages_zh_TW.properties b/src/main/resources/i18n/messages_zh_TW.properties new file mode 100644 index 0000000..dd24883 --- /dev/null +++ b/src/main/resources/i18n/messages_zh_TW.properties @@ -0,0 +1,26 @@ +gamecenter.title=\u904a\u6232\u4e2d\u5fc3 +gamecenter.welcome=\u6b61\u8fce, +gamecenter.will_text=\u5361\u865f: +gamecenter.button.back=\u8fd4\u56de +gamecenter.gamechance=\u904a\u6232\u6a5f\u6703: + +common.error_message.error_code_200=\u6210\u529f +common.error_message.error_code_401=\u8acb\u6c42\u5b89\u5168\u9a57\u8b49\u5931\u6557 +common.error_message.error_code_422=\u53c3\u6578\u932f\u8aa4 +common.error_message.error_code_301=\u904a\u6232\u672a\u958b\u653e +common.error_message.error_code_302=\u904a\u6232\u6b21\u6578\u5df2\u7528\u5b8c +common.error_message.error_code_303=\u932f\u8aa4\u7684\u8fd4\u56de\u7d50\u679c +common.error_message.error_code_304=\u6578\u64da\u91cd\u8907 +common.error_message.error_code_305=\u975e\u6cd5\u8acb\u6c42 +common.error_message.error_code_306=\u904a\u6232\u8d85\u6642\uff0c\u8acb\u91cd\u65b0\u9032\u5165 +common.error_message.error_code_307=\u5956\u52b1\u5df2\u6d3e\u767c\u5b8c\uff0c\u904a\u6232\u5df2\u7d50\u675f +common.error_message.error_code_701=\u8acb\u6c42\u932f\u8aa4 +common.error_message.error_code_308=\u904a\u620f\u8d85\u65f6, \u8bf7\u91cd\u65b0\u8fdb\u5165 +common.error_message.error_code_309=\u7372\u53d6\u6703\u54e1\u7b49\u7d1a\u5931\u6557 +common.error_message.error_code_310=\u904a\u6232\u5df2\u7d50\u675f, \u4e0d\u6703\u6263\u9664\u904a\u6232\u6a5f\u6703 + +common.no_game_chance=\u904a\u6232\u6b21\u6578\u5df2\u7528\u5b8c +common.get_game_chance_error=\u7372\u53d6\u904a\u6232\u6b21\u6578\u932f\u8aa4 +common.get_game_chance_request_error=\u7372\u53d6\u904a\u6232\u6b21\u6578\u8acb\u6c42\u932f\u8aa4 +common.no_game_available=\u6c92\u6709\u53ef\u53c3\u8207\u7684\u904a\u6232 +common.game_session_timeout=\u904a\u6232\u8d85\u6642 \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..3545290 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + %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/getBlock.json b/src/main/resources/mockup/app/getBlock.json new file mode 100644 index 0000000..227939b --- /dev/null +++ b/src/main/resources/mockup/app/getBlock.json @@ -0,0 +1,20 @@ +{ + "dataClass":"JSON", + "isList":false, + "dataContent":{ + "blocks":[ + { + "id":1, + "name":"Mass" + }, + { + "id":2, + "name":"IM" + }, + { + "id":3, + "name":"GM" + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getBookingsByNameId.json b/src/main/resources/mockup/app/getBookingsByNameId.json new file mode 100644 index 0000000..c21d4e5 --- /dev/null +++ b/src/main/resources/mockup/app/getBookingsByNameId.json @@ -0,0 +1,283 @@ +{ + "dataClass":"JSON", + "isList":false, + "dataContent":{ + "bookings":[ + { + "id":11, + "resort":"Macau", + "confirmationNo":"", + "roomType":"GTO", + "roomTypeDesc":"", + "arrivalDate":"", + "arrivalTime":"", + "departureDate":"", + "departureTime":"", + "resvStatus":"", + "blockCode":"", + "currentBalance":"", + "operaProfileId":"", + "bookedBy":"", + "authorizer":"", + "resvNameId":"", + "rateCode":"", + "marketSegment":"", + "sourceCode":"", + "room":"", + "sharer":"", + "groupId":"1", + "approveStatus":"WAITLIST" + }, + { + "id":10, + "resort":"Macau", + "confirmationNo":"", + "roomType":"GTO", + "roomTypeDesc":"", + "arrivalDate":"", + "arrivalTime":"", + "departureDate":"", + "departureTime":"", + "resvStatus":"", + "blockCode":"", + "currentBalance":"", + "operaProfileId":"", + "bookedBy":"", + "authorizer":"", + "resvNameId":"", + "rateCode":"", + "marketSegment":"", + "sourceCode":"", + "room":"", + "sharer":"", + "groupId":"1", + "approveStatus":"WAITLIST" + }, + { + "id":3, + "resort":"Macau", + "confirmationNo":"315586", + "roomType":"KE", + "roomTypeDesc":"", + "arrivalDate":"", + "arrivalTime":"", + "departureDate":"", + "departureTime":"", + "resvStatus":"CHECKED IN", + "blockCode":"", + "currentBalance":"", + "operaProfileId":"", + "bookedBy":"", + "authorizer":"", + "resvNameId":"", + "rateCode":"", + "marketSegment":"", + "sourceCode":"", + "room":"", + "sharer":"", + "groupId":"", + "approveStatus":"" + }, + { + "id":9, + "resort":"Cotai", + "confirmationNo":"", + "roomType":"TE", + "roomTypeDesc":"", + "arrivalDate":"", + "arrivalTime":"", + "departureDate":"", + "departureTime":"", + "resvStatus":"", + "blockCode":"", + "currentBalance":"", + "operaProfileId":"", + "bookedBy":"", + "authorizer":"", + "resvNameId":"", + "rateCode":"", + "marketSegment":"", + "sourceCode":"", + "room":"", + "sharer":"", + "groupId":"", + "approveStatus":"PENDING" + }, + { + "id":8, + "resort":"Macau", + "confirmationNo":"315585", + "roomType":"GTO", + "roomTypeDesc":"", + "arrivalDate":"", + "arrivalTime":"", + "departureDate":"", + "departureTime":"", + "resvStatus":"DUE IN", + "blockCode":"", + "currentBalance":"", + "operaProfileId":"", + "bookedBy":"", + "authorizer":"", + "resvNameId":"", + "rateCode":"", + "marketSegment":"", + "sourceCode":"", + "room":"", + "sharer":"", + "groupId":"", + "approveStatus":"" + }, + { + "id":7, + "resort":"Macau", + "confirmationNo":"315587", + "roomType":"TR", + "roomTypeDesc":"", + "arrivalDate":"", + "arrivalTime":"", + "departureDate":"", + "departureTime":"", + "resvStatus":"DUE IN", + "blockCode":"", + "currentBalance":"", + "operaProfileId":"", + "bookedBy":"", + "authorizer":"", + "resvNameId":"", + "rateCode":"", + "marketSegment":"", + "sourceCode":"", + "room":"", + "sharer":"", + "groupId":"", + "approveStatus":"" + }, + { + "id":6, + "resort":"Macau", + "confirmationNo":"315589", + "roomType":"GTO", + "roomTypeDesc":"", + "arrivalDate":"", + "arrivalTime":"", + "departureDate":"", + "departureTime":"", + "resvStatus":"DUE IN", + "blockCode":"", + "currentBalance":"", + "operaProfileId":"", + "bookedBy":"", + "authorizer":"", + "resvNameId":"", + "rateCode":"", + "marketSegment":"", + "sourceCode":"", + "room":"", + "sharer":"", + "groupId":"", + "approveStatus":"" + }, + { + "id":5, + "resort":"Macau", + "confirmationNo":"315590", + "roomType":"GTO", + "roomTypeDesc":"", + "arrivalDate":"", + "arrivalTime":"", + "departureDate":"", + "departureTime":"", + "resvStatus":"DUE IN", + "blockCode":"", + "currentBalance":"", + "operaProfileId":"", + "bookedBy":"", + "authorizer":"", + "resvNameId":"", + "rateCode":"", + "marketSegment":"", + "sourceCode":"", + "room":"", + "sharer":"", + "groupId":"", + "approveStatus":"" + }, + { + "id":4, + "resort":"Cotai", + "confirmationNo":"315588", + "roomType":"KE", + "roomTypeDesc":"", + "arrivalDate":"", + "arrivalTime":"", + "departureDate":"", + "departureTime":"", + "resvStatus":"DUE OUT", + "blockCode":"", + "currentBalance":"", + "operaProfileId":"", + "bookedBy":"", + "authorizer":"", + "resvNameId":"", + "rateCode":"", + "marketSegment":"", + "sourceCode":"", + "room":"", + "sharer":"", + "groupId":"", + "approveStatus":"" + }, + { + "id":2, + "resort":"Macau", + "confirmationNo":"315583", + "roomType":"GTO", + "roomTypeDesc":"", + "arrivalDate":"", + "arrivalTime":"", + "departureDate":"", + "departureTime":"", + "resvStatus":"CHECKED IN", + "blockCode":"", + "currentBalance":"", + "operaProfileId":"", + "bookedBy":"", + "authorizer":"", + "resvNameId":"", + "rateCode":"", + "marketSegment":"", + "sourceCode":"", + "room":"", + "sharer":"1101236", + "groupId":"2", + "approveStatus":"" + }, + { + "id":1, + "resort":"Macau", + "confirmationNo":"315582", + "roomType":"KE", + "roomTypeDesc":"", + "arrivalDate":"", + "arrivalTime":"", + "departureDate":"", + "departureTime":"", + "resvStatus":"CHECKED IN", + "blockCode":"", + "currentBalance":"", + "operaProfileId":"", + "bookedBy":"", + "authorizer":"", + "resvNameId":"", + "rateCode":"", + "marketSegment":"", + "sourceCode":"", + "room":"", + "sharer":"1101238", + "groupId":"2", + "approveStatus":"" + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getCriteria.json b/src/main/resources/mockup/app/getCriteria.json new file mode 100644 index 0000000..a017f47 --- /dev/null +++ b/src/main/resources/mockup/app/getCriteria.json @@ -0,0 +1,68 @@ +{ + "dataClass":"JSON", + "isList":false, + "dataContent":{ + "criterias":[ + { + "id":1, + "propertyId":1, + "roomId":1, + "blockId":1, + "bookDate":"", + "appChannel":100, + "isWaitlist":0, + "noSharer":0 + }, + { + "id":2, + "propertyId":1, + "roomId":1, + "blockId":2, + "bookDate":"", + "appChannel":50, + "isWaitlist":0, + "noSharer":0 + }, + { + "id":3, + "propertyId":1, + "roomId":1, + "blockId":3, + "bookDate":"", + "appChannel":20, + "isWaitlist":0, + "noSharer":0 + }, + { + "id":4, + "propertyId":1, + "roomId":2, + "blockId":1, + "bookDate":"", + "appChannel":100, + "isWaitlist":0, + "noSharer":0 + }, + { + "id":5, + "propertyId":1, + "roomId":2, + "blockId":2, + "bookDate":"", + "appChannel":50, + "isWaitlist":0, + "noSharer":0 + }, + { + "id":6, + "propertyId":1, + "roomId":2, + "blockId":3, + "bookDate":"", + "appChannel":20, + "isWaitlist":0, + "noSharer":0 + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getProperty.json b/src/main/resources/mockup/app/getProperty.json new file mode 100644 index 0000000..8c0eafe --- /dev/null +++ b/src/main/resources/mockup/app/getProperty.json @@ -0,0 +1,37 @@ +{ + "dataClass":"JSON", + "isList":false, + "dataContent":{ + "properties":[ + { + "id":1, + "name":"Macau", + "code":"MM", + "hotelCode":"Macau", + "category":"1" + }, + { + "id":2, + "name":"Cotai", + "code":"MC", + "hotelCode":"Cotai", + "category":"1" + }, + { + "id":3, + "name":"MO", + "code":"", + "hotelCode":"MO", + "category":"2" + }, + { + "id":4, + "name":"Asscot", + "code":"", + "hotelCode":"Asscot", + "category":"2" + } + + ] + } +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getRoom.json b/src/main/resources/mockup/app/getRoom.json new file mode 100644 index 0000000..5bdade5 --- /dev/null +++ b/src/main/resources/mockup/app/getRoom.json @@ -0,0 +1,53 @@ +{ + "dataClass":"JSON", + "isList":false, + "dataContent":{ + "rooms":[ + { + "id":1, + "propertyId":1, + "name":"Grand Room" + }, + { + "id":2, + "propertyId":1, + "name":"Deluxe" + }, + { + "id":3, + "propertyId":1, + "name":"Studio" + }, + { + "id":4, + "propertyId":1, + "name":"Resort Room" + }, + { + "id":5, + "propertyId":1, + "name":"1 Bedroom Suite" + }, + { + "id":6, + "propertyId":1, + "name":"2 Bedroom Suite" + }, + { + "id":7, + "propertyId":2, + "name":"Grand Room" + }, + { + "id":8, + "propertyId":2, + "name":"Deluxe" + }, + { + "id":9, + "propertyId":2, + "name":"Studio" + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getRoomType.json b/src/main/resources/mockup/app/getRoomType.json new file mode 100644 index 0000000..dd1ad21 --- /dev/null +++ b/src/main/resources/mockup/app/getRoomType.json @@ -0,0 +1,68 @@ +{ + "dataClass":"JSON", + "isList":false, + "dataContent":{ + "roomTypes":[ + { + "id":1, + "roomId":1, + "name":"GTO" + }, + { + "id":2, + "roomId":1, + "name":"GKO" + }, + { + "id":3, + "roomId":1, + "name":"GTU" + }, + { + "id":4, + "roomId":1, + "name":"GKU" + }, + { + "id":5, + "roomId":2, + "name":"STO" + }, + { + "id":6, + "roomId":2, + "name":"SKO" + }, + { + "id":7, + "roomId":2, + "name":"STO" + }, + { + "id":8, + "roomId":2, + "name":"SKO" + }, + { + "id":9, + "roomId":7, + "name":"GTO" + }, + { + "id":10, + "roomId":7, + "name":"GKO" + }, + { + "id":11, + "roomId":8, + "name":"STO" + }, + { + "id":12, + "roomId":8, + "name":"SKO" + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getSpecial.json b/src/main/resources/mockup/app/getSpecial.json new file mode 100644 index 0000000..f141870 --- /dev/null +++ b/src/main/resources/mockup/app/getSpecial.json @@ -0,0 +1,28 @@ +{ + "dataClass":"JSON", + "isList":false, + "dataContent":{ + "specials":[ + { + "id":1, + "name":"Smoking", + "code":"SM" + }, + { + "id":2, + "name":"Non-Smoking", + "code":"NS" + }, + { + "id":3, + "name":"High Floor", + "code":"HF" + }, + { + "id":4, + "name":"Near Elevator", + "code":"NE" + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoDashboard_1.json b/src/main/resources/mockup/app/getTodoDashboard_1.json new file mode 100644 index 0000000..3fec868 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoDashboard_1.json @@ -0,0 +1,76 @@ +{ + "StatusCode": "OK", + "StatusDescription": "BALABALABALABALADASHBOARD", + "Records": [ + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "Achievement", + "Identifier": 1, + "ItemName": "Team Target", + "Level": 2, + "CurrentValue": 1132045024.0, + "TargetValue": 12054210450.0 + }, + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "Achievement", + "Identifier": 2, + "ItemName": "Individual Target", + "Level": 2, + "CurrentValue": 10000.0, + "TargetValue": 30000.0 + }, + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "KPI", + "Identifier": 1, + "ItemName": "New & Reactive Theo", + "Level": null, + "CurrentValue": 10000.0, + "TargetValue": 30000.0 + }, + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "KPI", + "Identifier": 2, + "ItemName": "Direct offer Redemption Rate", + "Level": null, + "CurrentValue": 0.7, + "TargetValue": 0.8 + }, + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "KPI", + "Identifier": 3, + "ItemName": "Hosted Player Active Rate", + "Level": null, + "CurrentValue": 0.2, + "TargetValue": 0.8 + }, + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "KPI", + "Identifier": 4, + "ItemName": "Event Attendance (w/ gaming action)", + "Level": null, + "CurrentValue": 0.29, + "TargetValue": 0.2 + }, + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "DataAsOf", + "Identifier": 4, + "ItemName": "2025-10-14", + "Level": null, + "CurrentValue": null, + "TargetValue": null + } + ] +} diff --git a/src/main/resources/mockup/app/getTodoDashboard_2.json b/src/main/resources/mockup/app/getTodoDashboard_2.json new file mode 100644 index 0000000..3f4ecf4 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoDashboard_2.json @@ -0,0 +1,87 @@ +{ + "statusCode":"OK", + "statusDescription":"statusDescription Test", + "Records":[{ + "UserId":80002, + "UserSegment":"IM", + "Type":"Achievement", + "Identifier":1, + "Level":2, + "ItemName":"Team Target", + "CurrentValue":70456, + "TargetValue":100000 + }, + { + "UserId":80002, + "UserSegment":"IM", + "Type":"Achievement", + "Identifier":2, + "Level":2, + "ItemName":"Individual Target", + "CurrentValue":43328, + "TargetValue":100000 + }, + { + "UserId":80002, + "UserSegment":"IM", + "Type":"KPI", + "Identifier":3, + "Level":2, + "ItemName":"Reactive Player", + "CurrentValue":5, + "TargetValue":40 + }, + { + "UserId":80002, + "UserSegment":"IM", + "Type":"KPI", + "Identifier":4, + "Level":2, + "ItemName":"Active Primary Program", + "CurrentValue":20, + "TargetValue":100 + }, + { + "UserId":80002, + "UserSegment":"IM", + "Type":"Achievement", + "Identifier":5, + "Level":2, + "ItemName":"MF Turnover", + "CurrentValue":112, + "TargetValue":0 + }, + { + "UserId":80002, + "UserSegment":"IM", + "Type":"Achievement", + "Identifier":6, + "Level":2, + "ItemName":"IM Turnover", + "CurrentValue":8896, + "TargetValue":0 + }, + { + "UserId":80002, + "UserSegment":"IM", + "Type":"Achievement", + "Identifier":7, + "Level":2, + "ItemName":"NFRO Turnover", + "CurrentValue":1123, + "TargetValue":0 + }, + { + "UserId":80002, + "UserSegment":"IM", + "Type":"Achievement", + "Identifier":8, + "Level":2, + "ItemName":"MRI Turnover", + "CurrentValue":3323, + "TargetValue":0 + } + + ] + +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoDashboard_3.json b/src/main/resources/mockup/app/getTodoDashboard_3.json new file mode 100644 index 0000000..9d7abc0 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoDashboard_3.json @@ -0,0 +1,66 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "Records": [ + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "Achievement", + "Identifier": 1, + "ItemName": "Team Target", + "Level": 2, + "CurrentValue": 12125, + "TargetValue":null + }, + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "Achievement", + "Identifier": 2, + "ItemName": "Individual Target", + "Level": 2, + "CurrentValue": 65353, + "TargetValue":null + }, + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "KPI", + "Identifier": 1, + "ItemName": "New & Reactive Theo", + "Level": null, + "CurrentValue": 97843, + "TargetValue":0 + }, + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "KPI", + "Identifier": 2, + "ItemName": "Direct offer Redemption Rate", + "Level": null, + "CurrentValue": 0.7, + "TargetValue": null + }, + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "KPI", + "Identifier": 3, + "ItemName": "Hosted Player Active Rate", + "Level": null, + "CurrentValue": 0.2, + "TargetValue": null + }, + { + "UserId": 123457, + "UserSegment": "Mass", + "Type": "KPI", + "Identifier": 4, + "ItemName": "Event Attendance (w/ gaming action)", + "Level": null, + "CurrentValue": 4.0, + "TargetValue": null + } + ] +} diff --git a/src/main/resources/mockup/app/getTodoDashboard_4.json b/src/main/resources/mockup/app/getTodoDashboard_4.json new file mode 100644 index 0000000..fd480eb --- /dev/null +++ b/src/main/resources/mockup/app/getTodoDashboard_4.json @@ -0,0 +1,85 @@ +{ + "statusCode":"OK", + "statusDescription":"statusDescription Test", + "Records":[{ + "UserId":80004, + "UserSegment":"IM", + "Type":"Achievement", + "Identifier":1, + "Level":2, + "ItemName":"Team Target", + "CurrentValue":5151217626.07, + "TargetValue":null + }, + { + "UserId":80004, + "UserSegment":"IM", + "Type":"Achievement", + "Identifier":2, + "Level":2, + "ItemName":"Individual Target", + "CurrentValue":85428626.15, + "TargetValue":null + }, + { + "UserId":80004, + "UserSegment":"IM", + "Type":"KPI", + "Identifier":3, + "Level":2, + "ItemName":"Reactive Player", + "CurrentValue":5, + "TargetValue":null + }, + { + "UserId":80004, + "UserSegment":"IM", + "Type":"KPI", + "Identifier":4, + "Level":2, + "ItemName":"Active Primary Program", + "CurrentValue":20, + "TargetValue":null + }, + { + "UserId":80004, + "UserSegment":"IM", + "Type":"", + "Identifier":5, + "Level":2, + "ItemName":"MFTurnover", + "CurrentValue":112, + "TargetValue":null + }, + { + "UserId":80004, + "UserSegment":"IM", + "Type":"", + "Identifier":6, + "Level":2, + "ItemName":"IMTurnover", + "CurrentValue":8896, + "TargetValue":null + }, + { + "UserId":80004, + "UserSegment":"IM", + "Type":"", + "Identifier":7, + "Level":2, + "ItemName":"NFROTurnover", + "CurrentValue":1123, + "TargetValue":null + }, + { + "UserId":80004, + "UserSegment":"IM", + "Type":"", + "Identifier":8, + "Level":2, + "ItemName":"MRITurnover", + "CurrentValue":3323, + "TargetValue":null + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoDetail_1.json b/src/main/resources/mockup/app/getTodoDetail_1.json new file mode 100644 index 0000000..85543f4 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoDetail_1.json @@ -0,0 +1,67 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "PlayerId": "11111111", + "Date": "2025-07-24", + "TierName": "A", + "ChineseName": "陳大文", + "EnglishName": "Chan Tai Man", + "Salutation": "Mr.", + "Location": "19BB21 (Macau) 08:55", + "AvticePrgId": "C12345", + "PrgTO": 123465, + "PrgWin": 11000, + "TORIPercentage": 0.28, + "FrontMoney": 100000, + "TTO": 999999, + "ComBalance": 100101, + "R3MTO": 30000, + "R3MTOD": 35000, + "R3MWin": 1000, + "R3MTPD": 1000, + "R3MRIPercentage": 0.28, + "R6MTO": 20000, + "R6MTOD": 25000, + "R6MWin": 10000, + "R6MTPD": 10000, + "R6MRIPercentage": 0.26, + "TripTheo": 1000, + "TodayTheo": 100, + "R3MTheo": 1000, + "R6MTheo": 10000, + "ExpComm": 50000, + "NetCreditOutstanding": 225, + "UnsettledExpenses": 125851, + "TodayTO": 2000000, + "TodayTPD": 100, + "TodayWin": 500000, + "TripWin": 1000, + "TripTPD": 1000, + "TripRIPercentage": 0.15, + "YesterdayTO": 200000, + "YesterdayWin": 500000, + "YesterdayPlayHour": 4, + "AveragePlayHour": 3, + "Descriptions": [ + { + "ItemRecordId": 2, + "ItemRecordType": "Flight", + "ItemRecordDescription": "Jul 1 10:00 NX333 Macau - Beijing;" + }, + { + "ItemRecordId": 3, + "ItemRecordType": "Limo", + "ItemRecordDescription": "Jul 1 07:00 MGM Macau – MGM Cotai" + }, + { + "ItemRecordId": 4, + "ItemRecordType": "Limo", + "ItemRecordDescription": "Jul 1 08:00 MGM Cotai – Macau Airport" + }, + { + "ItemRecordId": 5, + "ItemRecordType": "Room", + "ItemRecordDescription": "Jun 25 – Jul 1 Macau – GR | Conf#12596936 | Sharer: Y" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoDetail_2.json b/src/main/resources/mockup/app/getTodoDetail_2.json new file mode 100644 index 0000000..81ccddc --- /dev/null +++ b/src/main/resources/mockup/app/getTodoDetail_2.json @@ -0,0 +1,63 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "PlayerId": "11111111", + "Date": "2025-07-24", + "TierName": "A", + "ChineseName": "陳大文", + "EnglishName": "Chan Tai Man", + "Salutation": "Mr.", + "Location": "19BB21 (Macau) 08:55", + "AvticePrgId": "C12345", + "PrgTO": 123465, + "PrgWin": 123151, + "TORIPercentage": 0.28, + "FrontMoney": 3357, + "TTO": 5241, + "ComBalance": 5814, + "R3MTO": 872, + "R3MTOD": 785, + "R3MWin": 457, + "R3MTPD": 9645, + "R3MRIPercentage": 0.28, + "R6MTO": 728, + "R6MTOD": 7854, + "R6MWin": 87525, + "R6MTPD": 87417, + "R6MRIPercentage": 0.26, + "ExpComm": 78521, + "NetCreditOutstanding": 0, + "UnsettledExpenses": 7854, + "TodayTO": 741, + "TodayTPD": 7455, + "TodayWin": 8563, + "TripWin": 84667, + "TripTPD": 2674, + "TripRIPercentage": 0.15, + "YesterdayTO": 28576654, + "YesterdayWin": 2544675, + "YesterdayPlayHour": 1, + "AveragePlayHour": 5, + "Descriptions": [ + { + "ItemRecordId": 2, + "ItemRecordType": "Flight", + "ItemRecordDescription": "Jul 1 10:00 NX333 Macau - Beijing" + }, + { + "ItemRecordId": 3, + "ItemRecordType": "Limo", + "ItemRecordDescription": "Jul 1 07:00 MGM Macau – MGM Cotai" + }, + { + "ItemRecordId": 4, + "ItemRecordType": "Limo", + "ItemRecordDescription": "Jul 1 08:00 MGM Cotai – Macau Airport" + }, + { + "ItemRecordId": 5, + "ItemRecordType": "Room", + "ItemRecordDescription": "Jun 25 – Jul 1 Macau – GR | Conf#12596936 | Sharer: Y" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoDetail_3.json b/src/main/resources/mockup/app/getTodoDetail_3.json new file mode 100644 index 0000000..510eb21 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoDetail_3.json @@ -0,0 +1,68 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "PlayerId": "11010003", + "Date": "2025-07-24", + "TierName": "A", + "ChineseName": "李美美", + "EnglishName": "Lei MeiMei", + "Salutation": "Mr.", + "Location": "19BB21 (Macau) 08:55", + "ActivePrgId": null, + "PrgTO": null, + "PrgWin": null, + "TORIPercentage": null, + "FrontMoney": null, + "TTO": null, + "01CompBalance": null, + "R3MTO": null, + "R3MTOD": null, + "R3MWin": "5200", + "R3MTPD": "150", + "R3MRIPercentage": 0.15, + "R6MTO": null, + "R6MTOD": null, + "R6MWin": "1500", + "R6MTPD": "5000", + "R6MRIPercentage": 0.15, + "ExpComm": null, + "NetCreditOutstanding": null, + "UnsettledExpenses": null, + "TodayTO": null, + "TodayTPD": "150", + "TodayWin": "130", + "TripWin": "1250", + "TripTPD": "1300", + "TripRIPercentage": 0.25, + "YesterdayTO": null, + "YesterdayWin": null, + "YesterdayPlayHour": null, + "AveragePlayHour": 3, + "Descriptions": [ + { + "ItemRecordId": 2, + "ItemRecordType": "Flight", + "ItemRecordDescription": "Jul 1 10:00 NX333 Macau - Beijing" + }, + { + "ItemRecordId": 1, + "ItemRecordType": "Offer", + "ItemRecordDescription": "20863 Play Upfront Jul1" + }, + { + "ItemRecordId": 5, + "ItemRecordType": "Room", + "ItemRecordDescription": "Jun 25 – Jul 1 Macau – GR | Conf#12596936 | Sharer: Y" + }, + { + "ItemRecordId": 4, + "ItemRecordType": "Room", + "ItemRecordDescription": "Jun 25 – Jul 2 Macau – GR | Conf#12596937" + }, + { + "ItemRecordId": 3, + "ItemRecordType": "Limo", + "ItemRecordDescription": "Jul 1 07:00 MGM Macau – MGM Cotai" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoDetail_4.json b/src/main/resources/mockup/app/getTodoDetail_4.json new file mode 100644 index 0000000..8677bb6 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoDetail_4.json @@ -0,0 +1,64 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "PlayerId": "11010002", + "Date": "2025-07-24", + "TierName": "A", + "ChineseName": "梁大文", + "EnglishName": "Leong Tai Man", + "Salutation": "Mr.", + "Location": "19BB21 (Macau) 08:55", + "ActivePrgId": null, + "PrgTO": null, + "PrgWin": null, + "TORIPercentage": null, + "FrontMoney": null, + "TTO": null, + "01CompBalance": null, + "R3MTO": null, + "R3MTOD": null, + "R3MWin": "1000", + "R3MTPD": "1000", + "R3MRIPercentage": 0.15, + "R6MTO": null, + "R6MTOD": null, + "R6MWin": "10000", + "R6MTPD": "10000", + "R6MRIPercentage": 0.15, + "ExpComm": null, + "NetCreditOutstanding": null, + "UnsettledExpenses": null, + "TodayTO": null, + "TodayTPD": "100", + "TodayWin": "100", + "TripWin": "1000", + "TripTPD": "1000", + "TripRIPercentage": 0.15, + "YesterdayTO": null, + "YesterdayWin": null, + "YesterdayPlayHour": null, + "AveragePlayHour": 3, + "Descriptions": [ + { + "ItemRecordId": 1, + "ItemRecordType": "Offer", + "ItemRecordDescription": "20863 Play Upfront Jul1" + }, + { + "ItemRecordId": 2, + "ItemRecordType": "Room", + "ItemRecordDescription": "Jun 25 – Jul 1 Macau – GR | Conf#12596936 | Sharer: Y" + }, + { + "ItemRecordId": 3, + "ItemRecordType": "Room", + "ItemRecordDescription": "Jun 25 – Jul 2 Macau – GR | Conf#12596937" + }, + + { + "ItemRecordId": 4, + "ItemRecordType": "Limo", + "ItemRecordDescription": "Jul 1 07:00 MGM Macau – MGM Cotai" + }, + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoList_1.json b/src/main/resources/mockup/app/getTodoList_1.json new file mode 100644 index 0000000..b67b9a5 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoList_1.json @@ -0,0 +1,216 @@ +{ + "StatusCode": "OK", + "StatusDescription": "BALABALABALABALA", + "Records": [ + { + "PlayerId": 11010001, + "Date": "Oct 20 (Mon)", + "ActionDate":"2025-10-20T00:00:00", + "OperaConfirmationNo":"Ascott;ForDefect001,Ascott;ForDefect002", + "TierName": "A", + "ChineseName": "ꐴóÎÄ", + "EnglishName": "Chan Tai Man", + "Salutation": "Mr.", + "HasArrivalRecord": 1, + "HasDepartureRecord": 0, + "HasInhouseRecord": 0, + "HasOthersRecord": 0, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": "15:00", + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Trip TPD: -|Trip Win: -| Trip Rl: 18%", + "Jun 24 - Jun 27 MO - GR | Conf#11251242 | Ref TPD: 20,000", + "Jun 24 - Jun 28 Ascott - GR | Conf#11251243 |" + ] + }, + { + "PlayerId": 11010002, + "Date": "Oct 20 (Mon)", + "ActionDate":"2025-10-20T00:00:00", + "OperaConfirmationNo":"Ascott;101235,MO;102226", + "TierName": "A", + "ChineseName": "Áº´óÎÄ", + "EnglishName": "Leong Tai Man", + "Salutation": "Mr.", + "HasArrivalRecord": 1, + "HasDepartureRecord": 1, + "HasInhouseRecord": 1, + "HasOthersRecord": 0, + "HasLimoRecord": 0, + "HasFlightRecord": 1, + "ETA": "16:40", + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "May 6 - May 7 Macau - GR | Ref TPD: 20,000", + "May 6 - May 8 Macau - GR | Ref TPD: 20,000 | Ref TPD: 20,000 | Ref TPD: 20,000" , + "May 6 - May 27 Macau - GR" + ] + }, + { + "PlayerId": 11010003, + "Date": "Oct 20 (Mon)", + "ActionDate":"2025-10-20T00:00:00", + "OperaConfirmationNo":"2387461,235612,2134897", + "TierName": "A", + "ChineseName": "ÀîÃÀÃÀ", + "EnglishName": "Lei MeiMei", + "Salutation": "MS.", + "HasArrivalRecord": 1, + "HasDepartureRecord": 0, + "HasInhouseRecord": 0, + "HasOthersRecord": 0, + "HasLimoRecord": 1, + "HasFlightRecord": 0, + "ETA": "15:59", + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "May 17 - May 19 Macau - GR", + "May 25 - May 27 Macau - GR" + ] + }, + { + "PlayerId": 11010004, + "Date": "Oct 20 (Mon)", + "ActionDate":"2025-10-20T00:00:00", + "OperaConfirmationNo":"Ascott;123345", + "TierName": "A", + "ChineseName": "ÀîÃÀÃÀ", + "EnglishName": "Rebecca", + "Salutation": "MS.", + "HasArrivalRecord": 0, + "HasDepartureRecord":1, + "HasInhouseRecord": 1, + "HasOthersRecord": 0, + "HasLimoRecord": 0, + "HasFlightRecord": 1, + "ETA": "17:00", + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "May 6 - May 7 Macau - GR | Ref TPD: 20,000", + "May 6 - May 8 Macau - GR", + ] + }, + { + "PlayerId": 11010005, + "Date": "Jun 26 (Wed)", + "ActionDate":"2025-10-16T00:00:00", + "OperaConfirmationNo":"2387461,235612,2134897", + "TierName": "A", + "ChineseName": "ÍõÀòÀò", + "EnglishName": "Wong Lily", + "Salutation": "MS.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 0, + "HasInhouseRecord": 0, + "HasOthersRecord": 1, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": "16:39", + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "May 6 - May 8 Macau - GR", + "May 6 - May 27 Macau - GR" + ] + }, + { + "PlayerId": 11010006, + "Date": "Jun 27 (Wed)", + "ActionDate":"2025-10-17T00:00:00", + "OperaConfirmationNo":"2387461,235612,2134897", + "TierName": "A", + "ChineseName": "³ÂÐñÎÄ", + "EnglishName": "Chen XuMan", + "Salutation": "MS.", + "HasArrivalRecord": 1, + "HasDepartureRecord": 0, + "HasInhouseRecord": 0, + "HasOthersRecord": 1, + "HasLimoRecord": 0, + "HasFlightRecord": 1, + "ETA": "11:22", + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "May 6 - May 7 Macau - GR | Ref TPD: 20,000 123 123 123 123123 123", + "May 6 - May 27 Macau - GR" + ] + }, + { + "PlayerId": 11010009, + "Date": "Jun 27 (Wed)", + "ActionDate":"2025-10-16T00:00:00", + "OperaConfirmationNo":"2387461,235612,2134897", + "TierName": "A", + "ChineseName": "³ÂÐñÎÄ", + "EnglishName": "Chen XuMan", + "Salutation": "MS.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 0, + "HasInhouseRecord": 0, + "HasOthersRecord": 1, + "HasLimoRecord": 0, + "HasFlightRecord": 1, + "ETA": "09:22", + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "May 6 - May 7 Macau - GR | Ref TPD: 20,000 123 123 123 123123 123", + "May 6 - May 27 Macau - GR" + ] + }, + { + "PlayerId": 11010007, + "Date": "Jun 27 (Wed)", + "ActionDate":"2025-10-15T00:00:00", + "OperaConfirmationNo":"2387461,235612,2134897", + "TierName": "A", + "ChineseName": "³ÂÐñÎÄ", + "EnglishName": "Chen XuMan", + "Salutation": "MS.", + "HasArrivalRecord": 1, + "HasDepartureRecord": 0, + "HasInhouseRecord": 0, + "HasOthersRecord": 1, + "HasLimoRecord": 0, + "HasFlightRecord": 1, + "ETA":"06:22", + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "May 6 - May 7 Macau - GR | Ref TPD: 20,000 123 123 123 123123 123", + "May 6 - May 27 Macau - GR" + ] + } + ] +} diff --git a/src/main/resources/mockup/app/getTodoList_2.json b/src/main/resources/mockup/app/getTodoList_2.json new file mode 100644 index 0000000..c049d18 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoList_2.json @@ -0,0 +1,58 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "Records": [ + { + "PlayerId": 22020001, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"MGM M;101235,MGM M;102226", + "TierName": "SUPREME", + "ChineseName": "张海斌", + "EnglishName": "HaiBin Zhang", + "Salutation": "Ms", + "HasArrivalRecord": 0, + "HasDepartureRecord": 0, + "HasInhouseRecord": 1, + "HasOthersRecord": 0, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": "13:45", + "BigWinFlag": 0, + "BigLossFlag": 1, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Jun 24 - Jun 27 MGM M - GR | Ref TPD: 20,000", + "Jun 24 - Jun 28 MGM C - GR" + ] + }, + { + "PlayerId": 11010001, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"MGM M;101235,MGM M;102226", + "TierName": "SUPREME 88", + "ChineseName": "劉明浩", + "EnglishName": "IVAN LAU", + "Salutation": "Mr.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 1, + "HasInhouseRecord": 1, + "HasOthersRecord": 0, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": "13:45", + "BigWinFlag": 1, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Jun 24 - Jun 27 Macau - GR | Ref TPD: 20,000", + "Jun 24 - Jun 28 Macau - GR" + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoList_3.json b/src/main/resources/mockup/app/getTodoList_3.json new file mode 100644 index 0000000..83be198 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoList_3.json @@ -0,0 +1,34 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "Records": [ + { + "PlayerId": 11010002, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"", + "TierName": "", + "ChineseName": "", + "EnglishName": "", + "Salutation": "", + "HasArrivalRecord": 0, + "HasDepartureRecord": 0, + "HasInhouseRecord": 0, + "HasOthersRecord": 0, + "HasLimoRecord": 0, + "HasFlightRecord": 0, + "ETA": "", + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 10000001, + "MA": "", + "Descriptions": [ + "acky Chueng Ticket to be collected C4 #11001961 CHAN TAI MAN (A) ", + "Make confirm call for Alan Walker event", + "Birthday offer will be expired on May 14 for #1101961 CHAN TAI MAN(A) ďD" + + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoList_4.json b/src/main/resources/mockup/app/getTodoList_4.json new file mode 100644 index 0000000..d22e348 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoList_4.json @@ -0,0 +1,86 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "Records": [ + { + "PlayerId": 11010001, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"12312312", + "TierName": "SUPREME 88", + "ChineseName": "劉明浩", + "EnglishName": "IVAN LAU", + "Salutation": "Mr.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 0, + "HasInhouseRecord": 1, + "HasOthersRecord": 0, + "HasLimoRecord": 1, + "HasFlightRecord": 0, + "ETA": "13:45", + "BigWinFlag": 1, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Active Prg:N", + "May7 - May8 Macau -GR | Conf#12312312 | Sharer: Y" + ] + }, + { + "PlayerId": 11010002, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"4562534,3456345,2345534", + "TierName": "PLATINUM", + "ChineseName": "李四", + "EnglishName": "ELIZABETH LEE", + "Salutation": "Mr.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 0, + "HasInhouseRecord": 0, + "HasOthersRecord": 0, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": "13:45", + "BigWinFlag": 0, + "BigLossFlag": 1, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Active Prg:Y", + "May7 - May8 Macau -GR | Conf#4562534 | Sharer: N", + "May7 - May8 Macau -GR | Conf#3456345 | Sharer: Y", + "May7 - May8 Macau -GR | Conf#2345534 | Sharer: N" + ] + }, + { + "PlayerId": 22020001, + "Date": "Jun 25 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"12596988", + "TierName": "SUPREME", + "ChineseName": "李莉莉", + "EnglishName": "LILY LEE", + "Salutation": "Ms", + "HasArrivalRecord": 0, + "HasDepartureRecord": 0, + "HasInhouseRecord": 1, + "HasOthersRecord": 1, + "HasLimoRecord": 1, + "HasFlightRecord": 0, + "ETA": "13:45", + "BigWinFlag": 1, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Active Prg:N", + "May7 - May8 Macau -GR | Conf#12596988 | Sharer: Y" + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoList_5.json b/src/main/resources/mockup/app/getTodoList_5.json new file mode 100644 index 0000000..e350c10 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoList_5.json @@ -0,0 +1,84 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "Records": [ + { + "PlayerId": 11010001, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"12312312", + "TierName": "SUPREME 88", + "ChineseName": "劉明浩", + "EnglishName": "IVAN LAU", + "Salutation": "Mr.", + "HasArrivalRecord": 1, + "HasDepartureRecord": 1, + "HasInhouseRecord": 0, + "HasOthersRecord": 0, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": "13:45", + "BigWinFlag": 1, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Active Prg:N", + "May7 - May8 Macau -GR | Conf#12312312 | Sharer: Y" + ] + }, + { + "PlayerId": 11010002, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"4562534", + "TierName": "PLATINUM", + "ChineseName": "李思思", + "EnglishName": "SISI LEE", + "Salutation": "Mr.", + "HasArrivalRecordHasArrivalRecord": 1, + "HasDepartureRecord": 0, + "HasInhouseRecord": 0, + "HasOthersRecord": 1, + "HasLimoRecord": 0, + "HasFlightRecord": 1, + "ETA": "13:45", + "BigWinFlag": 0, + "BigLossFlag": 1, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Active Prg:Y", + "May7 - May8 Macau -GR | Conf#4562534| Sharer: N" + ] + }, + { + "PlayerId": 22020001, + "Date": "Jun 25 (Tue)", + "ActionDate":"2025-07-25", + "OperaConfirmationNo":"12596988", + "TierName": "SUPREME", + "ChineseName": "李莉莉", + "EnglishName": "LILY LEE", + "Salutation": "Ms", + "HasArrivalRecord": 1, + "HasDepartureRecord": 0, + "HasInhouseRecord": 1, + "HasOthersRecord": 0, + "HasLimoRecord": 0, + "HasFlightRecord": 0, + "ETA": "13:45", + "BigWinFlag": 1, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Active Prg:N", + "May7 - May8 Macau -GR | Conf#12596988| Sharer: N" + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoList_6.json b/src/main/resources/mockup/app/getTodoList_6.json new file mode 100644 index 0000000..abe3a18 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoList_6.json @@ -0,0 +1,83 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "Records": [ + { + "PlayerId": 11010001, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"", + "TierName": "SUPREME 88", + "ChineseName": "彭超", + "EnglishName": "Chao Peng", + "Salutation": "Mr.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 0, + "HasInhouseRecord": 0, + "HasOthersRecord": 0, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": null, + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 1, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Test1Test1Test1#REC#Test2Test2Test2" + ] + }, + { + "PlayerId": 11010002, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"", + "TierName": "PLATINUM", + "ChineseName": "李四", + "EnglishName": "ELIZABETH LEE", + "Salutation": "Mr.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 1, + "HasInhouseRecord": 0, + "HasOthersRecord": 0, + "HasLimoRecord": 0, + "HasFlightRecord": 1, + "ETA": null, + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Trip TPD:200,000|Trip Win:100,000|Trip RI%:10%#REC#TESTTSETTEST" + ] + }, + { + "PlayerId": 22020001, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"", + "TierName": "SUPREME", + "ChineseName": "李莉莉", + "EnglishName": "LILY LEE", + "Salutation": "Ms", + "HasArrivalRecord": 1, + "HasDepartureRecord": 1, + "HasInhouseRecord": 1, + "HasOthersRecord": 1, + "HasLimoRecord": 0, + "HasFlightRecord": 0, + "ETA": null, + "BigWinFlag": 1, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Trip TPD:200,000|Trip Win:100,000|Trip RI%:80%", + "Remark1 | Remark2", + "Offer:Y" + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoList_7.json b/src/main/resources/mockup/app/getTodoList_7.json new file mode 100644 index 0000000..d33e32d --- /dev/null +++ b/src/main/resources/mockup/app/getTodoList_7.json @@ -0,0 +1,58 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "Records": [ + { + "PlayerId": 11010001, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"", + "TierName": "SUPREME 88", + "ChineseName": "劉明浩", + "EnglishName": "IVAN LAU", + "Salutation": "Mr.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 0, + "HasInhouseRecord": 1, + "HasOthersRecord": 0, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": "13:45", + "BigWinFlag": 1, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Prg TPD:2,000,000|Prg Win:100,000|A 0.1:200,000", + "T/O RI%:10%|Yesterday Playhour:3hours" + ] + }, + { + "PlayerId": 22020001, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"", + "TierName": "SUPREME", + "ChineseName": "李莉莉", + "EnglishName": "LILY LEE", + "Salutation": "Ms", + "HasArrivalRecord": 1, + "HasDepartureRecord": 0, + "HasInhouseRecord": 1, + "HasOthersRecord": 0, + "HasLimoRecord": 0, + "HasFlightRecord": 0, + "ETA": "13:45", + "BigWinFlag": 1, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "Prg TPD:2,000,000|Prg Win:100,000|A 0.1:200,000", + "T/O RI%:10%|Yesterday Playhour:3hours" + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/getTodoList_8.json b/src/main/resources/mockup/app/getTodoList_8.json new file mode 100644 index 0000000..477c8f0 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoList_8.json @@ -0,0 +1,87 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "Records": [ + { + "PlayerId": 11010001, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"7893245", + "TierName": "A", + "ChineseName": "戴大卫", + "EnglishName": "David Dai", + "Salutation": "Mr.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 1, + "HasInhouseRecord": 0, + "HasOthersRecord": 1, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": "13:45", + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "May 6 - May 7 Macau - GR | Ref TPD: 20,000" + ] + }, + { + "PlayerId": 22020001, + "Date": "Jun 24 (Tue)", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"1925312,124774,238446", + "TierName": "A", + "ChineseName": "李荣浩", + "EnglishName": "Ronghao Lee", + "Salutation": "Mr.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 0, + "HasInhouseRecord": 1, + "HasOthersRecord": 0, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": null, + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "May 6 - May 7 Macau - GR | Ref TPD: 20,000", + "May 6 - May 8 Macau - GR", + "May 6 - May 27 Macau - GR" + ] + }, + { + "PlayerId": 13333333, + "Date": "Jun 25 (Wed)", + "ActionDate":"2025-07-25", + "OperaConfirmationNo":"651234,912843,451263", + "ActionDate":"2025-07-24", + "OperaConfirmationNo":"12312312", + "TierName": "A", + "ChineseName": "夏美美", + "EnglishName": "Meimei Xia", + "Salutation": "MS.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 1, + "HasInhouseRecord": 0, + "HasOthersRecord": 0, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": null, + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "May 6 - May 7 Macau - GR | Ref TPD: 20,000", + "May 6 - May 8 Macau - GR", + "May 6 - May 27 Macau - GR" + ] + } + ] +} diff --git a/src/main/resources/mockup/app/getTodoList_9.json b/src/main/resources/mockup/app/getTodoList_9.json new file mode 100644 index 0000000..f8c1be2 --- /dev/null +++ b/src/main/resources/mockup/app/getTodoList_9.json @@ -0,0 +1,84 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "Records": [ + { + "PlayerId": 11010001, + "Date": "Jun 24 (Tue)", + "ActionDate":"2026-07-24T00:00:00", + "OperaConfirmationNo":"MGM SIT - Integration Testing;00003709COCON,ProperName;201212ASDASD", + "TierName": "A", + "ChineseName": "戴大卫", + "EnglishName": "David Dai", + "Salutation": "Mr.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 1, + "HasInhouseRecord": 0, + "HasOthersRecord": 1, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": null, + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "May 6 - May 7 Macau - GR | Ref TPD: 20,000" + ] + }, + { + "PlayerId": 22020001, + "Date": "Jun 24 (Tue)", + "ActionDate":"2026-07-24T00:00:00", + "OperaConfirmationNo":"MGM SIT - Integration Testing;00003567COCON", + "TierName": "A", + "ChineseName": "李荣浩", + "EnglishName": "Ronghao Lee", + "Salutation": "Mr.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 0, + "HasInhouseRecord": 1, + "HasOthersRecord": 0, + "HasLimoRecord": 1, + "HasFlightRecord": 1, + "ETA": null, + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "MAId": 123456, + "MA": "Jojo Chan", + "Descriptions": [ + "May 6 - May 7 Macau - GR | Ref TPD: 20,000", + "May 6 - May 8 Macau - GR", + "May 6 - May 27 Macau - GR" + ] + }, + { + "PlayerId": 12357795, + "Date": "Jul 2 (Thu)", + "ActionDate": "2026-07-02T00:00:00", + "TierName": "G", + "ChineseName": "", + "EnglishName": "SA, CHI", + "Salutation": "Ms.", + "HasArrivalRecord": 0, + "HasDepartureRecord": 0, + "HasInhouseRecord": 0, + "HasOthersRecord": 0, + "HasLimoRecord": 0, + "HasFlightRecord": 0, + "ETA": null, + "BigWinFlag": 0, + "BigLossFlag": 0, + "NoPlayFlag": 0, + "OperaConfirmationNo": "MGM SIT - Integration Testing;00003483COCON", + "MAId": null, + "MA": null, + "Descriptions": [ + "Ref TPD: 5,280", + "Jul 2 - Jul 3 MGM SIT - Integration Testing - RSR | Conf#00003483COCON | Sharer: N" + ] + } + ] +} diff --git a/src/main/resources/mockup/app/playerProfile.json b/src/main/resources/mockup/app/playerProfile.json new file mode 100644 index 0000000..fe4c9e7 --- /dev/null +++ b/src/main/resources/mockup/app/playerProfile.json @@ -0,0 +1,11 @@ +{ + "dataClass":"JSON", + "isList":false, + "dataContent":{ + "playerId":"1101234", + "name":"CHAN, TAI MAN", + "tier":"SUPREME", + "salutation":"Mr", + "nameIds":[1001,1002,1003] + } +} \ No newline at end of file diff --git a/src/main/resources/mockup/app/submitETA.json b/src/main/resources/mockup/app/submitETA.json new file mode 100644 index 0000000..9a9d1ac --- /dev/null +++ b/src/main/resources/mockup/app/submitETA.json @@ -0,0 +1,4 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success" +} diff --git a/src/main/resources/mockup/http/check_player_tpd_all_pass.json b/src/main/resources/mockup/http/check_player_tpd_all_pass.json new file mode 100644 index 0000000..9d8cba1 --- /dev/null +++ b/src/main/resources/mockup/http/check_player_tpd_all_pass.json @@ -0,0 +1,18 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "PlayerId": "", + "RoomCategory": "GR", + "BookingDate": "2024/06/01", + "BookingDateQualificationFlag": 1 + }, + { + "PlayerId": "", + "RoomCategory": "GR", + "BookingDate": "2024/06/02", + "BookingDateQualificationFlag": 1 + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/check_player_tpd_not_all_pass.json b/src/main/resources/mockup/http/check_player_tpd_not_all_pass.json new file mode 100644 index 0000000..af55ebf --- /dev/null +++ b/src/main/resources/mockup/http/check_player_tpd_not_all_pass.json @@ -0,0 +1,18 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "PlayerId": "", + "RoomCategory": "GR", + "BookingDate": "2024/06/01", + "BookingDateQualificationFlag": 0 + }, + { + "PlayerId": "", + "RoomCategory": "GR", + "BookingDate": "2024/06/02", + "BookingDateQualificationFlag": 0 + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/check_player_tpd_partly_pass.json b/src/main/resources/mockup/http/check_player_tpd_partly_pass.json new file mode 100644 index 0000000..44f2f28 --- /dev/null +++ b/src/main/resources/mockup/http/check_player_tpd_partly_pass.json @@ -0,0 +1,18 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "PlayerId": "", + "RoomCategory": "GR", + "BookingDate": "2024/06/01", + "BookingDateQualificationFlag": 0 + }, + { + "PlayerId": "", + "RoomCategory": "GR", + "BookingDate": "2024/06/02", + "BookingDateQualificationFlag": 1 + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res1.json b/src/main/resources/mockup/http/get_player_profile_api_res1.json new file mode 100644 index 0000000..04f5cb4 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res1.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"11010001", + "UserId":"", + "TierName":"SUPREME 88", + "TierId":34, + "ChineseName":"劉明浩", + "EnglishName":"IVAN LAU", + "Salutation":"Mr." , + "CodedFlag":1, + "PlayerPhone":"123456", + "PhoneType":"MOBILE", + "Birthday":"2000-01-01", + "Country":"CN", + "State":"MO", + "Passport":"123456", + "Language":"E" +} diff --git a/src/main/resources/mockup/http/get_player_profile_api_res10.json b/src/main/resources/mockup/http/get_player_profile_api_res10.json new file mode 100644 index 0000000..06e9575 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res10.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"", + "UserId":"", + "TierName":null, + "TierId":null, + "ChineseName":null, + "EnglishName":null, + "Salutation":null , + "CodedFlag":null, + "PlayerPhone":null, + "PhoneType":null, + "Birthday":null, + "Country":null, + "State":null, + "Passport":null, + "Language":null +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res11.json b/src/main/resources/mockup/http/get_player_profile_api_res11.json new file mode 100644 index 0000000..5edc3fc --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res11.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"22020001", + "UserId":"", + "TierName":"SUPREME", + "TierId":17, + "ChineseName":"李莉莉", + "EnglishName":"LILY LEE", + "Salutation":"Ms" , + "CodedFlag":1 , + "PlayerPhone":"223461", + "PhoneType":"MOBILE", + "Birthday":"2000-02-11", + "Country":"CN", + "State":"MO", + "Passport":"223461", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res12.json b/src/main/resources/mockup/http/get_player_profile_api_res12.json new file mode 100644 index 0000000..694f40b --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res12.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"22020002", + "UserId":"", + "TierName":"PLATINUM", + "TierId":16, + "ChineseName":"楊陽", + "EnglishName":"SUNNY YUENG", + "Salutation":"Ms" , + "CodedFlag":1 , + "PlayerPhone":"223411", + "PhoneType":"MOBILE", + "Birthday":"2000-02-11", + "Country":"CN", + "State":"MO", + "Passport":"223411", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res13.json b/src/main/resources/mockup/http/get_player_profile_api_res13.json new file mode 100644 index 0000000..43864d8 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res13.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"22020003", + "UserId":"", + "TierName":"SUPREME", + "TierId":17, + "ChineseName":"陳皮", + "EnglishName":"PETER CHAN", + "Salutation":"Ms" , + "CodedFlag":1 , + "PlayerPhone":"323461", + "PhoneType":"MOBILE", + "Birthday":"2000-03-21", + "Country":"CN", + "State":"MO", + "Passport":"323461", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res14.json b/src/main/resources/mockup/http/get_player_profile_api_res14.json new file mode 100644 index 0000000..fa7e53b --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res14.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"22020004", + "UserId":"", + "TierName":null, + "TierId":null, + "ChineseName":null, + "EnglishName":null, + "Salutation":null , + "CodedFlag":0, + "PlayerPhone":null, + "PhoneType":null, + "Birthday":null, + "Country":null, + "State":null, + "Passport":null, + "Language":null +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res15.json b/src/main/resources/mockup/http/get_player_profile_api_res15.json new file mode 100644 index 0000000..7b50741 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res15.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"22020005", + "UserId":"", + "TierName":"SUPREME", + "TierId":17, + "ChineseName":"費達", + "EnglishName":"HIFAI FEI", + "Salutation":"Mr" , + "CodedFlag":1, + "PlayerPhone":"163461", + "PhoneType":"MOBILE", + "Birthday":"1986-06-12", + "Country":"CN", + "State":"MO", + "Passport":"163461", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res16.json b/src/main/resources/mockup/http/get_player_profile_api_res16.json new file mode 100644 index 0000000..ce64314 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res16.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"22020006", + "UserId":"", + "TierName":"ALPHA", + "TierId":17, + "ChineseName":"許老七", + "EnglishName":"SEVEN HUI", + "Salutation":"Mr" , + "CodedFlag":1, + "PlayerPhone":"423461", + "PhoneType":"MOBILE", + "Birthday":"1998-08-20", + "Country":"CN", + "State":"MO", + "Passport":"423461", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res17.json b/src/main/resources/mockup/http/get_player_profile_api_res17.json new file mode 100644 index 0000000..993b7a5 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res17.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"22020007", + "UserId":"", + "TierName":"SUPREME 88 IM", + "TierId":35, + "ChineseName":"梁大大", + "EnglishName":"MAY LUENG", + "Salutation":"Ms" , + "CodedFlag":1 , + "PlayerPhone":"422462", + "PhoneType":"MOBILE", + "Birthday":"1988-08-20", + "Country":"CN", + "State":"MO", + "Passport":"422462", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res18.json b/src/main/resources/mockup/http/get_player_profile_api_res18.json new file mode 100644 index 0000000..ddd5a78 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res18.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"22020008", + "UserId":"", + "TierName":"PLATINUM IM", + "TierId":26, + "ChineseName":"鄭凱凱", + "EnglishName":"KATE CHENG", + "Salutation":"Ms" , + "CodedFlag":1 , + "PlayerPhone":"222461", + "PhoneType":"MOBILE", + "Birthday":"1978-03-20", + "Country":"CN", + "State":"MO", + "Passport":"222461", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res19.json b/src/main/resources/mockup/http/get_player_profile_api_res19.json new file mode 100644 index 0000000..9a72489 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res19.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"22020009", + "UserId":"", + "TierName":"PLATINUM IM", + "TierId":17, + "ChineseName":"郭成", + "EnglishName":"PHILIP KOWK", + "Salutation":"Mr" , + "CodedFlag":1 , + "PlayerPhone":"663232", + "PhoneType":"MOBILE", + "Birthday":"1990-04-20", + "Country":"CN", + "State":"MO", + "Passport":"663232", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res2.json b/src/main/resources/mockup/http/get_player_profile_api_res2.json new file mode 100644 index 0000000..f45f0bb --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res2.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"11010002", + "UserId":"", + "TierName":"PLATINUM", + "TierId":16, + "ChineseName":"李四", + "EnglishName":"ELIZABETH LEE", + "Salutation":"Ms" , + "CodedFlag":1, + "PlayerPhone":"123457", + "PhoneType":"MOBILE", + "Birthday":"2000-01-02", + "Country":"CN", + "State":"MO", + "Passport":"123457", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res20.json b/src/main/resources/mockup/http/get_player_profile_api_res20.json new file mode 100644 index 0000000..128b6b9 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res20.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"33030001", + "UserId":"", + "TierName":"SUPREME 88", + "TierId":34, + "ChineseName":"李明浩", + "EnglishName":"KEVIN LEE", + "Salutation":"Mr" , + "CodedFlag":1 , + "PlayerPhone":"633232", + "PhoneType":"MOBILE", + "Birthday":"1992-03-06", + "Country":"CN", + "State":"MO", + "Passport":"633232", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res21.json b/src/main/resources/mockup/http/get_player_profile_api_res21.json new file mode 100644 index 0000000..4866291 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res21.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"33030002", + "UserId":"", + "TierName":"PLATINUM", + "TierId":16, + "ChineseName":"王四", + "EnglishName":"HELLEN WONG", + "Salutation":"Ms" , + "CodedFlag":1, + "PlayerPhone":"333232", + "PhoneType":"MOBILE", + "Birthday":"1990-04-20", + "Country":"CN", + "State":"MO", + "Passport":"663237", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res22.json b/src/main/resources/mockup/http/get_player_profile_api_res22.json new file mode 100644 index 0000000..8126be2 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res22.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"33030003", + "UserId":"", + "TierName":"SUPREME", + "TierId":17, + "ChineseName":"王五", + "EnglishName":"WILLIAMS WONG", + "Salutation":"Mr" , + "CodedFlag":1 , + "PlayerPhone":"883232", + "PhoneType":"MOBILE", + "Birthday":"1990-04-20", + "Country":"CN", + "State":"MO", + "Passport":"663288", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res23.json b/src/main/resources/mockup/http/get_player_profile_api_res23.json new file mode 100644 index 0000000..f9b8139 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res23.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"33030004", + "UserId":"", + "TierName":null, + "TierId":null, + "ChineseName":null, + "EnglishName":null, + "Salutation":null , + "CodedFlag":0 , + "PlayerPhone":null, + "PhoneType":null, + "Birthday":null, + "Country":null, + "State":null, + "Passport":null, + "Language":null +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res24.json b/src/main/resources/mockup/http/get_player_profile_api_res24.json new file mode 100644 index 0000000..6289aef --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res24.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"33030005", + "UserId":"", + "TierName":"SUPREME", + "TierId":17, + "ChineseName":"刘珊珊", + "EnglishName":"CANDY LAU", + "Salutation":"Ms" , + "CodedFlag":1 , + "PlayerPhone":"883231", + "PhoneType":"MOBILE", + "Birthday":"1993-02-20", + "Country":"CN", + "State":"MO", + "Passport":"663688", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res25.json b/src/main/resources/mockup/http/get_player_profile_api_res25.json new file mode 100644 index 0000000..faf0410 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res25.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"33030006", + "UserId":"", + "TierName":"ALPHA", + "TierId":17, + "ChineseName":"許老六", + "EnglishName":"CHIMING HUI", + "Salutation":"Mr" , + "CodedFlag":1 , + "PlayerPhone":"873232", + "PhoneType":"MOBILE", + "Birthday":"1991-01-20", + "Country":"CN", + "State":"MO", + "Passport":"663636", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res26.json b/src/main/resources/mockup/http/get_player_profile_api_res26.json new file mode 100644 index 0000000..344782e --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res26.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"33030007", + "UserId":"", + "TierName":"SUPREME 88 IM", + "TierId":35, + "ChineseName":"梁小", + "EnglishName":"GARY LUENG", + "Salutation":"Mr" , + "CodedFlag":1, + "PlayerPhone":"886231", + "PhoneType":"MOBILE", + "Birthday":"1983-09-19", + "Country":"CN", + "State":"MO", + "Passport":"6636322", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res27.json b/src/main/resources/mockup/http/get_player_profile_api_res27.json new file mode 100644 index 0000000..7acdf09 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res27.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"33030008", + "UserId":"", + "TierName":"PLATINUM IM", + "TierId":26, + "ChineseName":"鄭雯雯", + "EnglishName":"WENDY CHENG", + "Salutation":"Ms" , + "CodedFlag":1, + "PlayerPhone":"883232", + "PhoneType":"MOBILE", + "Birthday":"1983-02-23", + "Country":"CN", + "State":"MO", + "Passport":"6623266", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res28.json b/src/main/resources/mockup/http/get_player_profile_api_res28.json new file mode 100644 index 0000000..709c71c --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res28.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"33030009", + "UserId":"", + "TierName":"SUPREME", + "TierId":17, + "ChineseName":"张明明", + "EnglishName":"SAM CHUENG", + "Salutation":"Mr" , + "CodedFlag":1, + "PlayerPhone":"881132", + "PhoneType":"MOBILE", + "Birthday":"1989-11-19", + "Country":"CN", + "State":"MO", + "Passport":"6626236", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res29.json b/src/main/resources/mockup/http/get_player_profile_api_res29.json new file mode 100644 index 0000000..8bc8f20 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res29.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"1212121", + "UserId":"", + "TierName":"SUPREME", + "TierId":17, + "ChineseName":"张明明", + "EnglishName":"SAM CHUENG", + "Salutation":"Mr" , + "CodedFlag":1, + "PlayerPhone":"881132", + "PhoneType":"MOBILE", + "Birthday":"1989-11-19", + "Country":"CN", + "State":"MO", + "Passport":"6626236", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res3.json b/src/main/resources/mockup/http/get_player_profile_api_res3.json new file mode 100644 index 0000000..5077b07 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res3.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"11010003", + "UserId":"", + "TierName":"SUPREME", + "TierId":17, + "ChineseName":"王五", + "EnglishName":"WILLIAMS WONG", + "Salutation":"Mr" , + "CodedFlag":1 , + "PlayerPhone":"123458", + "PhoneType":"MOBILE", + "Birthday":"2000-01-03", + "Country":"CN", + "State":"MO", + "Passport":"123458", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res30.json b/src/main/resources/mockup/http/get_player_profile_api_res30.json new file mode 100644 index 0000000..8d9fb32 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res30.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"12357817", + "UserId":"", + "TierName":"SUPREME", + "TierId":17, + "ChineseName":"张明明", + "EnglishName":"SAM CHUENG", + "Salutation":"Mr" , + "CodedFlag":1, + "PlayerPhone":"881132", + "PhoneType":"MOBILE", + "Birthday":"1989-11-19", + "Country":"CN", + "State":"MO", + "Passport":"6626236", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res31.json b/src/main/resources/mockup/http/get_player_profile_api_res31.json new file mode 100644 index 0000000..04e5a49 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res31.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"11335577", + "UserId":"", + "TierName":"SUPREME", + "TierId":17, + "ChineseName":"张明明", + "EnglishName":"SAM CHUENG", + "Salutation":"Mr" , + "CodedFlag":1, + "PlayerPhone":"881132", + "PhoneType":"MOBILE", + "Birthday":"1989-11-19", + "Country":"CN", + "State":"MO", + "Passport":"6626236", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res4.json b/src/main/resources/mockup/http/get_player_profile_api_res4.json new file mode 100644 index 0000000..5d205e1 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res4.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"11010004", + "UserId":"", + "TierName":null, + "TierId":null, + "ChineseName":null, + "EnglishName":null, + "Salutation":null , + "CodedFlag":0 , + "PlayerPhone":null, + "PhoneType":null, + "Birthday":null, + "Country":null, + "State":null, + "Passport":null, + "Language":null +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res5.json b/src/main/resources/mockup/http/get_player_profile_api_res5.json new file mode 100644 index 0000000..43cde29 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res5.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"11010005", + "UserId":"", + "TierName":"SUPREME", + "TierId":17, + "ChineseName":"陳珊珊", + "EnglishName":"SANDY CHAN", + "Salutation":"Ms" , + "CodedFlag":1 , + "PlayerPhone":"123459", + "PhoneType":"MOBILE", + "Birthday":"2000-01-04", + "Country":"CN", + "State":"MO", + "Passport":"123459", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res6.json b/src/main/resources/mockup/http/get_player_profile_api_res6.json new file mode 100644 index 0000000..1a43cbb --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res6.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"11010006", + "UserId":"", + "TierName":"ALPHA", + "TierId":17, + "ChineseName":"許老六", + "EnglishName":"ANDY HUI", + "Salutation":"Mr" , + "CodedFlag":1 , + "PlayerPhone":"123460", + "PhoneType":"MOBILE", + "Birthday":"2000-01-06", + "Country":"CN", + "State":"MO", + "Passport":"123460", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res7.json b/src/main/resources/mockup/http/get_player_profile_api_res7.json new file mode 100644 index 0000000..f543a80 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res7.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"11010007", + "UserId":"", + "TierName":"SUPREME 88 IM", + "TierId":35, + "ChineseName":"梁小小", + "EnglishName":"CINDY LUENG", + "Salutation":"Ms" , + "CodedFlag":1 , + "PlayerPhone":"123461", + "PhoneType":"MOBILE", + "Birthday":"2000-01-11", + "Country":"CN", + "State":"MO", + "Passport":"123461", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res8.json b/src/main/resources/mockup/http/get_player_profile_api_res8.json new file mode 100644 index 0000000..0db4dd5 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res8.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"11010008", + "UserId":"", + "TierName":"PLATINUM IM", + "TierId":26, + "ChineseName":"鄭雯雯", + "EnglishName":"SAMMY CHENG", + "Salutation":"Ms" , + "CodedFlag":1 , + "PlayerPhone":"123462", + "PhoneType":"MOBILE", + "Birthday":"2000-01-12", + "Country":"CN", + "State":"MO", + "Passport":"123462", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_player_profile_api_res9.json b/src/main/resources/mockup/http/get_player_profile_api_res9.json new file mode 100644 index 0000000..1fa6a75 --- /dev/null +++ b/src/main/resources/mockup/http/get_player_profile_api_res9.json @@ -0,0 +1,19 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "PlayerId":"11010009", + "UserId":"", + "TierName":"SUPREME", + "TierId":17, + "ChineseName":"黎明明", + "EnglishName":"ALLEN LAI", + "Salutation":"Mr" , + "CodedFlag":1 , + "PlayerPhone":"123463", + "PhoneType":"MOBILE", + "Birthday":"2000-01-16", + "Country":"CN", + "State":"MO", + "Passport":"123463", + "Language":"E" +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res1.json b/src/main/resources/mockup/http/get_user_info_api_res1.json new file mode 100644 index 0000000..3dc7b14 --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res1.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "NULL", + "UserName": "Andy.lau", + "Property": "MGM MACAU", + "Division": "International Marketing", + "Department": "Regional Marketing", + "Section": "Slot", + "Block": "GM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res10.json b/src/main/resources/mockup/http/get_user_info_api_res10.json new file mode 100644 index 0000000..64b250f --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res10.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "90007", + "UserName": "MGM User07", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res11.json b/src/main/resources/mockup/http/get_user_info_api_res11.json new file mode 100644 index 0000000..b3b7cc7 --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res11.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "90008", + "UserName": "MGM User08", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res12.json b/src/main/resources/mockup/http/get_user_info_api_res12.json new file mode 100644 index 0000000..52eb106 --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res12.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "90009", + "UserName": "MGM User09", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res13.json b/src/main/resources/mockup/http/get_user_info_api_res13.json new file mode 100644 index 0000000..1c0fe6c --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res13.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "80003", + "UserName": "LC", + "Property": "MGM MACAU", + "Division": "Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res14.json b/src/main/resources/mockup/http/get_user_info_api_res14.json new file mode 100644 index 0000000..9051b38 --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res14.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "80004", + "UserName": "Feng", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res15.json b/src/main/resources/mockup/http/get_user_info_api_res15.json new file mode 100644 index 0000000..46bb711 --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res15.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "80005", + "UserName": "Beyond", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res16.json b/src/main/resources/mockup/http/get_user_info_api_res16.json new file mode 100644 index 0000000..a4b6a18 --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res16.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "800004", + "UserName": "Feng", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res17.json b/src/main/resources/mockup/http/get_user_info_api_res17.json new file mode 100644 index 0000000..3152d19 --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res17.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "80008", + "UserName": "Corso", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "123123123" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res2.json b/src/main/resources/mockup/http/get_user_info_api_res2.json new file mode 100644 index 0000000..4535d53 --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res2.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "90001", + "UserName": "IWIWIN", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res3.json b/src/main/resources/mockup/http/get_user_info_api_res3.json new file mode 100644 index 0000000..48a5dc1 --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res3.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "90002", + "UserName": "Judy Wong", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res4.json b/src/main/resources/mockup/http/get_user_info_api_res4.json new file mode 100644 index 0000000..260de8e --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res4.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "80001", + "UserName": "Steven Lei", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res5.json b/src/main/resources/mockup/http/get_user_info_api_res5.json new file mode 100644 index 0000000..2630aff --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res5.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "80002", + "UserName": "JianQi Yan", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res6.json b/src/main/resources/mockup/http/get_user_info_api_res6.json new file mode 100644 index 0000000..8ee8890 --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res6.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "90003", + "UserName": "Octavio", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res7.json b/src/main/resources/mockup/http/get_user_info_api_res7.json new file mode 100644 index 0000000..18acccd --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res7.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "90004", + "UserName": "Inmei Lei", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res8.json b/src/main/resources/mockup/http/get_user_info_api_res8.json new file mode 100644 index 0000000..1b696af --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res8.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "90005", + "UserName": "MGM User05", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/get_user_info_api_res9.json b/src/main/resources/mockup/http/get_user_info_api_res9.json new file mode 100644 index 0000000..4ff6f9d --- /dev/null +++ b/src/main/resources/mockup/http/get_user_info_api_res9.json @@ -0,0 +1,16 @@ +{ + "StatusCode": "OK", + "StatusDescription": "SUCCESS", + "Records": [ + { + "UserId": "90006", + "UserName": "MGM User06", + "Property": "MGM MACAU", + "Division": "VIP Marketing", + "Department": "Global Marketing", + "Section": "Global Marketing", + "Block": "CM", + "Phone": "85366988316" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/non_mgm_booking_no_record.json b/src/main/resources/mockup/http/non_mgm_booking_no_record.json new file mode 100644 index 0000000..92c4f56 --- /dev/null +++ b/src/main/resources/mockup/http/non_mgm_booking_no_record.json @@ -0,0 +1,6 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "Records": [ + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/http/non_mgm_booking_with_record.json b/src/main/resources/mockup/http/non_mgm_booking_with_record.json new file mode 100644 index 0000000..32a2a8a --- /dev/null +++ b/src/main/resources/mockup/http/non_mgm_booking_with_record.json @@ -0,0 +1,24 @@ +{ + "StatusCode": "OK", + "StatusDescription": "Success", + "Records": [ + { + "ConfirmationNumber": "5747311", + "Property":"Ascott", + "RoomTypeStatusCode":"CKI", + "CheckInDate": "2024-07-11", + "CheckOutDate":"2024-07-19", + "CreatedBy":"Judy Wong", + "CreatedOn":"2024-07-09" + }, + { + "ConfirmationNumber": "5747312", + "Property":"Ascott", + "RoomTypeStatusCode":"CKI", + "CheckInDate": "2024-07-20", + "CheckOutDate":"2024-07-21", + "CreatedBy":"Judy Wong", + "CreatedOn":"2024-07-18" + } + ] +} \ No newline at end of file diff --git a/src/main/resources/mockup/mgm_api_mockup_def.json b/src/main/resources/mockup/mgm_api_mockup_def.json new file mode 100644 index 0000000..f877d2c --- /dev/null +++ b/src/main/resources/mockup/mgm_api_mockup_def.json @@ -0,0 +1,380 @@ +[ +{ + "apiLink": "GetReportingLinePlayerInfo", + "resultIndex":9, + "mockupRules":[ + { + "condiction":"playerId==11010001", + "fileIndex":0 + }, + { + "condiction":"playerId==11010002", + "fileIndex":1 + }, + { + "condiction":"playerId==11010003", + "fileIndex":2 + }, + { + "condiction":"playerId==11010004", + "fileIndex":3 + }, + { + "condiction":"playerId==11010005", + "fileIndex":4 + }, + { + "condiction":"playerId==11010006", + "fileIndex":5 + }, + { + "condiction":"playerId==11010007", + "fileIndex":6 + }, + { + "condiction":"playerId==11010008", + "fileIndex":7 + }, + { + "condiction":"playerId==11010009", + "fileIndex":8 + }, + { + "condiction":"playerId==22020001", + "fileIndex":10 + }, + { + "condiction":"playerId==22020002", + "fileIndex":11 + }, + { + "condiction":"playerId==22020003", + "fileIndex":12 + }, + { + "condiction":"playerId==22020004", + "fileIndex":13 + }, + { + "condiction":"playerId==22020005", + "fileIndex":14 + }, + { + "condiction":"playerId==22020006", + "fileIndex":15 + }, + { + "condiction":"playerId==22020007", + "fileIndex":16 + }, + { + "condiction":"playerId==22020008", + "fileIndex":17 + }, + { + "condiction":"playerId==22020009", + "fileIndex":18 + }, + { + "condiction":"playerId==33030001", + "fileIndex":19 + }, + { + "condiction":"playerId==33030002", + "fileIndex":20 + }, + { + "condiction":"playerId==33030003", + "fileIndex":21 + }, + { + "condiction":"playerId==33030004", + "fileIndex":22 + }, + { + "condiction":"playerId==33030005", + "fileIndex":23 + }, + { + "condiction":"playerId==33030006", + "fileIndex":24 + }, + { + "condiction":"playerId==33030007", + "fileIndex":25 + }, + { + "condiction":"playerId==33030008", + "fileIndex":26 + }, + { + "condiction":"playerId==33030009", + "fileIndex":27 + }, + { + "condiction":"playerId==1212121", + "fileIndex":28 + }, + { + "condiction":"playerId==12357817", + "fileIndex":29 + }, + { + "condiction":"playerId==11335577", + "fileIndex":30 + } + ], + "resultFiles": [ + "/mockup/http/get_player_profile_api_res1.json", + "/mockup/http/get_player_profile_api_res2.json", + "/mockup/http/get_player_profile_api_res3.json", + "/mockup/http/get_player_profile_api_res4.json", + "/mockup/http/get_player_profile_api_res5.json", + "/mockup/http/get_player_profile_api_res6.json", + "/mockup/http/get_player_profile_api_res7.json", + "/mockup/http/get_player_profile_api_res8.json", + "/mockup/http/get_player_profile_api_res9.json", + "/mockup/http/get_player_profile_api_res10.json", + "/mockup/http/get_player_profile_api_res11.json", + "/mockup/http/get_player_profile_api_res12.json", + "/mockup/http/get_player_profile_api_res13.json", + "/mockup/http/get_player_profile_api_res14.json", + "/mockup/http/get_player_profile_api_res15.json", + "/mockup/http/get_player_profile_api_res16.json", + "/mockup/http/get_player_profile_api_res17.json", + "/mockup/http/get_player_profile_api_res18.json", + "/mockup/http/get_player_profile_api_res19.json", + "/mockup/http/get_player_profile_api_res20.json", + "/mockup/http/get_player_profile_api_res21.json", + "/mockup/http/get_player_profile_api_res22.json", + "/mockup/http/get_player_profile_api_res23.json", + "/mockup/http/get_player_profile_api_res24.json", + "/mockup/http/get_player_profile_api_res25.json", + "/mockup/http/get_player_profile_api_res26.json", + "/mockup/http/get_player_profile_api_res27.json", + "/mockup/http/get_player_profile_api_res28.json", + "/mockup/http/get_player_profile_api_res29.json", + "/mockup/http/get_player_profile_api_res30.json", + "/mockup/http/get_player_profile_api_res31.json" + ] +}, +{ + "apiLink": "GetLoginUserInfo", + "resultIndex":0, + "mockupRules":[ + { + "condiction":"userId==90001", + "fileIndex":1 + }, + { + "condiction":"userId==90002", + "fileIndex":2 + }, + { + "condiction":"userId==90003", + "fileIndex":5 + }, + { + "condiction":"userId==90004", + "fileIndex":6 + }, + { + "condiction":"userId==90005", + "fileIndex":7 + }, + { + "condiction":"userId==90006", + "fileIndex":8 + }, + { + "condiction":"userId==90007", + "fileIndex":9 + }, + { + "condiction":"userId==90008", + "fileIndex":10 + }, + { + "condiction":"userId==90009", + "fileIndex":11 + }, + { + "condiction":"userId==80001", + "fileIndex":3 + }, + { + "condiction":"userId==80002", + "fileIndex":4 + }, + { + "condiction":"userId==80003", + "fileIndex":12 + }, + { + "condiction":"userId==80004", + "fileIndex":13 + }, + { + "condiction":"userId==80005", + "fileIndex":14 + }, + { + "condiction":"userId==80008", + "fileIndex":16 + } + ], + "resultFiles": [ + "/mockup/http/get_user_info_api_res1.json", + "/mockup/http/get_user_info_api_res2.json", + "/mockup/http/get_user_info_api_res3.json", + "/mockup/http/get_user_info_api_res4.json", + "/mockup/http/get_user_info_api_res5.json", + "/mockup/http/get_user_info_api_res6.json", + "/mockup/http/get_user_info_api_res7.json", + "/mockup/http/get_user_info_api_res8.json", + "/mockup/http/get_user_info_api_res9.json", + "/mockup/http/get_user_info_api_res10.json", + "/mockup/http/get_user_info_api_res11.json", + "/mockup/http/get_user_info_api_res12.json", + "/mockup/http/get_user_info_api_res13.json", + "/mockup/http/get_user_info_api_res14.json", + "/mockup/http/get_user_info_api_res15.json", + "/mockup/http/get_user_info_api_res16.json", + "/mockup/http/get_user_info_api_res17.json" + ] +}, +{ + "apiLink": "GetRoomBookingQualificationStatus", + "resultIndex":0, + "mockupRules":[ + { + "condiction":"playerId==11010001 || playerId==11010005 || playerId==11010008 || playerId==11010009 || playerId==22020001 || playerId==22020005 || playerId==22020008 || playerId==22020009 || playerId==33030001 || playerId==33030005 || playerId==33030008 || playerId==33030009 || playerId==1212121", + "fileIndex":0 + }, + { + "condiction":"playerId==11010002 || playerId==11010006 || playerId==22020002 || playerId==22020006 || playerId==33030002 || playerId==33030006", + "fileIndex":1 + }, + { + "condiction":"playerId==11010003 || playerId==11010007 || playerId==22020003 || playerId==22020007 || playerId==33030003 || playerId==33030007", + "fileIndex":2 + } + ], + "resultFiles": [ + "/mockup/http/check_player_tpd_all_pass.json", + "/mockup/http/check_player_tpd_not_all_pass.json", + "/mockup/http/check_player_tpd_partly_pass.json" + ] +}, +{ + "apiLink": "GetNonMGMRoomBookingRecord", + "resultIndex":0, + "mockupRules":[ + { + "condiction":"playerId==11010005", + "fileIndex":1 + } + ], + "resultFiles": [ + "/mockup/http/non_mgm_booking_no_record.json", + "/mockup/http/non_mgm_booking_with_record.json" + ] +}, +{ + "apiLink":"GetDashboardData", + "resultIndex":0, + "mockupRules":[ + { + "condiction":"UserId==80001", + "fileIndex":0 + }, + { + "condiction":"UserId==80002", + "fileIndex":1 + }, + { + "condiction":"UserId==80003", + "fileIndex":2 + }, + { + "condiction":"UserId==80004", + "fileIndex":3 + } + ], + "resultFiles":[ + "/mockup/app/getTodoDashboard_1.json", + "/mockup/app/getTodoDashboard_2.json", + "/mockup/app/getTodoDashboard_3.json", + "/mockup/app/getTodoDashboard_4.json" + ] +}, +{ + "apiLink":"GetTodolistDetails", + "resultIndex":0, + "mockupRules":[ + { + "condiction":"PlayerId==11010001", + "fileIndex":0 + }, + { + "condiction":"PlayerId==11010002", + "fileIndex":1 + }, + { + "condiction":"PlayerId==11010003", + "fileIndex":2 + } + ], + "resultFiles":[ + "/mockup/app/getTodoDetail_1.json", + "/mockup/app/getTodoDetail_2.json", + "/mockup/app/getTodoDetail_3.json" + ] +}, +{ + "apiLink":"GetTodolist", + "resultIndex":0, + "mockupRules":[ + { + "condiction":"TodoCategory == Arrival && TodoSegment == MASS", + "fileIndex":8 + }, + { + "condiction":"TodoCategory == Others", + "fileIndex":2 + }, + { + "condiction":"TodoCategory == Arrival && TodoSegment == IM", + "fileIndex":3 + }, + { + "condiction":"TodoCategory == Departure && TodoSegment == IM", + "fileIndex":4 + }, + { + "condiction":"TodoCategory == Inhouse && TodoSegment == MASS", + "fileIndex":5 + }, + { + "condiction":"TodoCategory == Inhouse && TodoSegment == IM", + "fileIndex":6 + }, + { + "condiction":"TodoCategory == Departure && TodoSegment == MASS", + "fileIndex":7 + } + ], + "resultFiles":[ + "/mockup/app/getTodoList_1.json", + "/mockup/app/getTodoList_2.json", + "/mockup/app/getTodoList_3.json", + "/mockup/app/getTodoList_4.json", + "/mockup/app/getTodoList_5.json", + "/mockup/app/getTodoList_6.json", + "/mockup/app/getTodoList_7.json", + "/mockup/app/getTodoList_8.json", + "/mockup/app/getTodoList_9.json" + ] +} + +] diff --git a/src/main/resources/mockup/system/login.json b/src/main/resources/mockup/system/login.json new file mode 100644 index 0000000..56aec66 --- /dev/null +++ b/src/main/resources/mockup/system/login.json @@ -0,0 +1,16 @@ +{ + "dataClass":"JSON", + "isList":false, + "dataContent":{ + "id":"admin", + "userCode":"admin", + "userName":"admin", + "roles":[ + { + "id":"admin_role", + "roleName":"admin role", + "roleCode":"ADMIN_ROLE" + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/static/WM.png b/src/main/resources/static/WM.png new file mode 100644 index 0000000..e0b3372 Binary files /dev/null and b/src/main/resources/static/WM.png differ diff --git a/src/main/resources/static/WP.png b/src/main/resources/static/WP.png new file mode 100644 index 0000000..dfc32ba Binary files /dev/null and b/src/main/resources/static/WP.png differ diff --git a/src/main/resources/static/logon1.png b/src/main/resources/static/logon1.png new file mode 100644 index 0000000..049dda1 Binary files /dev/null and b/src/main/resources/static/logon1.png differ