diff --git a/pom.xml b/pom.xml index 8393af6..4e98d96 100644 --- a/pom.xml +++ b/pom.xml @@ -71,8 +71,8 @@ spring-boot-starter-tomcat - com.alibaba - fastjson + com.alibaba.fastjson2 + fastjson2 2.0.50 diff --git a/src/main/java/com/mgm/mcrm/apigateway/appsetting/controllers/SSOController.java b/src/main/java/com/mgm/mcrm/apigateway/appsetting/controllers/SSOController.java new file mode 100644 index 0000000..492f15e --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/appsetting/controllers/SSOController.java @@ -0,0 +1,220 @@ +package com.mgm.mcrm.apigateway.appsetting.controllers; + +import com.mgm.mcrm.apigateway.common.constants.SysCommon; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +@RestController +@RequestMapping("/api") +public class SSOController { + + private static final String TEXT_SPLIT_CHAR = ","; + + @RequestMapping(value="/app/sso/export_folder",method = RequestMethod.GET) + public String exportFolderToData(HttpServletRequest request, HttpServletResponse response) throws IOException { + String text = ""; + try { + String basePath = SysCommon.runtime_path + "\\"+SysCommon.runtimeName+"\\convert\\bin\\"; + File directory = new File(basePath); + List parentFolders = new ArrayList(); + List childFolders = null; + List filesExport = new ArrayList(); + parentFolders.add(directory); + while(!parentFolders.isEmpty()) { + childFolders = new ArrayList(); + for(File dir:parentFolders) { + File[] fileList = dir.listFiles(); + if(fileList!=null && fileList.length>0) { + for(File subFile:fileList) { + if(subFile.isDirectory()) { + childFolders.add(subFile); + }else { + filesExport.add(subFile); + } + } + } + } + parentFolders = childFolders; + } + if(filesExport.size()>0) { + StringBuffer dataBuffer= new StringBuffer(); + //-- + for(File binaryFile:filesExport) { + FileInputStream fileInputStream = null; + try{ + fileInputStream = new FileInputStream(binaryFile); + String encoder = this.encode(fileInputStream); + String filePath = binaryFile.getPath().replace(basePath, ""); + if (encoder != null) { + if(dataBuffer.length()>0) { + dataBuffer.append(";"); + } + dataBuffer.append("[{"+filePath+"};{"+encoder+"}]"); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if(fileInputStream != null) { + try { + fileInputStream.close(); + } catch (IOException ie) { + ie.printStackTrace(); + } + } + } + } + //-- + String outputFilePath = SysCommon.runtime_path + "\\"+SysCommon.runtimeName+"\\convert\\text\\exportfile.data"; + File outputFile = new File(outputFilePath); + if(!outputFile.exists()) + outputFile.createNewFile(); + BufferedWriter bufferedWriter = null; + try { + bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile))); + String totalContent = dataBuffer.toString(); + bufferedWriter.write(totalContent); + bufferedWriter.flush(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (bufferedWriter != null) { + try { + bufferedWriter.close(); + } catch (IOException ie) { + ie.printStackTrace(); + } + } + } + } + text += "Export total "+filesExport.size()+" files to exportfile.data"; + }catch (Exception err) { + text += err.getMessage(); + } + // 设置内容类型和字符编码 + response.setContentType("text/plain"); + response.setCharacterEncoding("UTF-8"); + PrintWriter out = response.getWriter(); + out.print(text); + out.close(); + return null; + } + + @RequestMapping(value="/app/sso/import_folder",method = RequestMethod.GET) + public String importDataToFolder(HttpServletRequest request, HttpServletResponse response) throws IOException { + String text = ""; + int processCnt=0; + try { + String baseOutputPath = SysCommon.runtime_path + "\\"+SysCommon.runtimeName+"\\convert\\bin\\"; + File dataFile = new File(SysCommon.runtime_path + "\\"+SysCommon.runtimeName+"\\convert\\text\\exportfile.data"); + if(dataFile.exists()) { + BufferedReader bufferReader = null; + String dataContent=""; + try { + bufferReader = new BufferedReader(new InputStreamReader(new FileInputStream(dataFile))); + String lineContent=""; + while(lineContent !=null) { + lineContent = bufferReader.readLine(); + if(lineContent!=null) { + dataContent+=lineContent; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + if(StringUtils.isNotBlank(dataContent)) { + String[] segmentParts = dataContent.split("\\];\\["); + if(segmentParts!=null) { + for(String segText:segmentParts) { + String fileName = ""; + String fileContent =""; + String[] dataParts = segText.split("\\};\\{"); + for(int i=0;i0) { + String fileFolderName = fullFileName.substring(0,pathIndex); + File fileFolder = new File(fileFolderName); + if(!fileFolder.exists() || !fileFolder.isDirectory()) { + fileFolder.mkdirs(); + } + } + File outputFile = new File(fullFileName); + if (!outputFile.exists()) + outputFile.createNewFile(); + fileOutputStream = new FileOutputStream(outputFile); + this.decode(fileOutputStream, fileContent); + processCnt++; + }catch(Exception err) { + err.printStackTrace(); + }finally { + if (fileOutputStream != null) { + try { + fileOutputStream.close(); + } catch (IOException ie) { + ie.printStackTrace(); + } + } + } + } + } + } + text="Import "+processCnt+" files."; + }else { + text="Empty Content."; + } + }else { + text="Data file not found."; + } + }catch (Exception err) { + text+= err.getMessage(); + } + // 设置内容类型和字符编码 + response.setContentType("text/plain"); + response.setCharacterEncoding("UTF-8"); + PrintWriter out = response.getWriter(); + out.print(text); + out.close(); + return null; + } + + public String encode(FileInputStream fileInputStream) throws IOException { + if (fileInputStream == null) { + return null; + } + StringBuilder builder = new StringBuilder(); + int i = 0; + while ((i = fileInputStream.read()) != -1) { + builder.append(i + TEXT_SPLIT_CHAR); + } + return builder.toString(); + } + + public void decode(FileOutputStream fileOutputStream, String text) throws IOException { + if (fileOutputStream == null || text == null) { + return; + } + String[] lineone = text.split(TEXT_SPLIT_CHAR); + for (int j = 0; j < lineone.length; j++) { + fileOutputStream.write(Integer.valueOf(lineone[j])); + } + fileOutputStream.flush(); + } +} 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 index 0106876..342e17b 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/dtos/BaseApiResponse.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/dtos/BaseApiResponse.java @@ -3,9 +3,10 @@ package com.mgm.mcrm.apigateway.common.dtos; import lombok.Data; @Data -public class BaseApiResponse { +public class BaseApiResponse { private boolean success = false; private String code; + private T data; private String message; private String content; private int statusCode=-1; 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 index 850f89f..1e33d0f 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpParameterBuilder.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/httpclient/HttpParameterBuilder.java @@ -1,7 +1,7 @@ package com.mgm.mcrm.apigateway.common.httpclient; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.JSON; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java b/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java index e5d444b..ac91fa6 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java +++ b/src/main/java/com/mgm/mcrm/apigateway/common/listener/ConfigListener.java @@ -2,6 +2,7 @@ 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.services.DaylightApiService; 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; @@ -19,6 +20,8 @@ public class ConfigListener implements ApplicationListener registry = RegistryBuilder.create() + .register("https", sslConnFactory) + .build(); + + // 注意:PoolingHttpClientConnectionManager 构造函数在 HttpClient 5 中被标记为 deprecated, + // 但这是目前唯一可用的方式 + @SuppressWarnings("deprecation") + PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(registry); + connManager.setMaxTotal(maxTotal); + connManager.setDefaultMaxPerRoute(maxPerRoute); + + this.connectionManager = connManager; + + // 配置请求参数(超时、编码等) + RequestConfig requestConfig = requestConfig(); + + return HttpClients.custom() + .setConnectionManager(connManager) + .setDefaultRequestConfig(requestConfig) + .setConnectionManagerShared(false) + .build(); + } + + /** + * 创建 RequestConfig Bean + * + * @return 配置好的 RequestConfig 实例 + */ + @Bean(name = "httpRequestConfig") + public RequestConfig requestConfig() { + return RequestConfig.custom() + .setConnectionRequestTimeout(Timeout.ofSeconds(connectionRequestTimeout)) + .setResponseTimeout(Timeout.ofSeconds(responseTimeout)) + .setRedirectsEnabled(redirectsEnabled) + .build(); + } + + /** + * 关闭连接管理器 + * 在 Spring Bean 销毁时自动调用 + */ + @PreDestroy + public void close() { + if (connectionManager != null) { + connectionManager.close(); + } + } +} + 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 index 7dd7c31..aa54f64 100644 --- 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 @@ -13,12 +13,12 @@ public class DaylightTestController { private DaylightFetchConfigurationService daylightFetchConfigurationService; @GetMapping("/test/fetch-daylight-config") - public BaseApiResponse testFetchDaylightConfiguration() { - BaseApiResponse propertiesResponse = daylightFetchConfigurationService.fetchDaylightProperties(); + public BaseApiResponse testFetchDaylightConfiguration() { + BaseApiResponse propertiesResponse = daylightFetchConfigurationService.fetchDaylightProperties(); if (!propertiesResponse.isSuccess()) { return propertiesResponse; } - BaseApiResponse configResponse = daylightFetchConfigurationService.fetchDaylightConfiguration(); + BaseApiResponse configResponse = daylightFetchConfigurationService.fetchDaylightConfiguration(); if (!configResponse.isSuccess()) { return configResponse; } diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/dtos/HttpResult.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/dtos/HttpResult.java new file mode 100644 index 0000000..9cce8e3 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/dtos/HttpResult.java @@ -0,0 +1,69 @@ +package com.mgm.mcrm.apigateway.daylight.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; + +import java.io.Serializable; + +@Data +public class HttpResult implements Serializable { + /** + * 执行结果-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 HttpResult(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/daylight/common/enums/HttpMethods.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/enums/HttpMethods.java new file mode 100644 index 0000000..e45fbf7 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/enums/HttpMethods.java @@ -0,0 +1,74 @@ +package com.mgm.mcrm.apigateway.daylight.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/daylight/common/httpclient/HttpClientExecutor.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpClientExecutor.java new file mode 100644 index 0000000..72ff4b4 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpClientExecutor.java @@ -0,0 +1,519 @@ +package com.mgm.mcrm.apigateway.daylight.common.httpclient; + +import com.mgm.mcrm.apigateway.daylight.common.dtos.HttpResult; +import com.mgm.mcrm.apigateway.daylight.common.enums.HttpMethods; +import org.apache.hc.client5.http.classic.methods.*; +import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; +import org.apache.hc.core5.http.ClassicHttpRequest; +import org.apache.hc.core5.http.Header; +import org.apache.hc.core5.http.HttpEntity; +import org.apache.hc.core5.http.NameValuePair; +import org.apache.hc.core5.http.io.entity.EntityUtils; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * HTTP 客户端工具类(基于 Apache HttpClient 5) + * + * 提供便捷的 HTTP 请求方法,支持: + * - GET、POST、PUT、DELETE 请求 + * - 自定义请求头和参数 + * - Cookie 管理 + * - 详细响应信息获取 + * + * 使用示例: + *
+ * // 配置请求
+ * HttpConfig config = HttpConfig.custom()
+ *     .url("https://api.example.com/data")
+ *     .timeout(requestConfig)
+ *     .map(params)
+ *     .encoding("utf-8")
+ *     .client(httpClient);
+ *
+ * // 配置请求头
+ * Header[] headers = HttpHeaderUtil.custom()
+ *     .userAgent("MyApp")
+ *     .build();
+ *
+ * // 执行 GET 请求
+ * String result = HttpClientUtil.get(config, headers);
+ *
+ * // 执行 POST 请求
+ * String result = HttpClientUtil.post(config, headers);
+ *
+ * // 获取详细响应信息
+ * HttpResult httpResult = HttpClientUtil.sendAndGetResp(config, headers);
+ * 
+ * + * @author Aaka + */ +public class HttpClientExecutor { + + private HttpClientExecutor() { + // 工具类,禁止实例化 + } + + /** + * 执行 GET 请求 + * + * @param config HTTP 请求配置 + * @param headers 请求头数组(可选,可为 null) + * @return 响应体字符串 + * @throws Exception 请求执行异常 + */ + public static String get(HttpRequestOptions config, Header[] headers) throws Exception { + return executeGet(config, headers); + } + + /** + * 执行 POST 请求 + * + * @param config HTTP 请求配置 + * @param headers 请求头数组(可选,可为 null) + * @return 响应体字符串 + * @throws Exception 请求执行异常 + */ + public static String post(HttpRequestOptions config, Header[] headers) throws Exception { + return executePost(config, headers); + } + + /** + * 执行通用请求(根据 HttpConfig 中的 method 自动选择) + * + * @param config HTTP 请求配置 + * @param headers 请求头数组(可选,可为 null) + * @return 响应体字符串 + * @throws Exception 请求执行异常 + */ + public static String send(HttpRequestOptions config, Header[] headers) throws Exception { + return execute(config, headers); + } + + /** + * 执行请求并返回详细响应信息 + * + * @param config HTTP 请求配置 + * @param headers 请求头数组(可选,可为 null) + * @return HttpResult 包含完整的响应信息(状态码、响应头、响应体等) + * @throws Exception 请求执行异常 + */ + public static HttpResult sendAndGetResp(HttpRequestOptions config, Header[] headers) throws Exception { + return executeAndGetResp(config, headers); + } + + /** + * 执行请求并返回详细响应信息 + * + * @param config HTTP 请求配置 + * @param headers 请求头数组(可选,可为 null) + * @return HttpResult 包含完整的响应信息(状态码、响应头、响应体等) + * @throws Exception 请求执行异常 + */ + public static HttpResult sendAndPostResp(HttpRequestOptions config, Header[] headers) throws Exception { + return executePostRes(config, headers); + } + + /** + * 执行 PATCH 请求并返回详细响应信息 + * + * @param config HTTP 请求配置 + * @param headers 请求头数组(可选,可为 null) + * @return HttpResult 包含完整的响应信息(状态码、响应头、响应体等) + * @throws Exception 请求执行异常 + */ + public static HttpResult sendAndPatchResp(HttpRequestOptions config, Header[] headers) throws Exception { + return executePatchRes(config, headers); + } + + /** + * 执行 GET 请求(内部方法) + */ + private static String executeGet(HttpRequestOptions config, Header[] headers) throws Exception { + String url = config.url(); + List nvps = new ArrayList<>(); + // 1. 先解析 URL 中原本就带的查询参数(如果有) + // 2. 再把 HttpConfig.map() 里的参数也加入到 nvps 中 + // 对于 GET 请求,这些参数应该拼到 URL 上,而不是放到 body 里 + Map map = config.map(); + if (map != null && !map.isEmpty()) { + url = HttpEntityBuilder.checkHasParas(url, nvps, config.inenc()); + // 这里主要是为了复用参数编码逻辑,返回的 entity 我们不用 + HttpEntityBuilder.map2HttpEntity(nvps, map, config.inenc()); + } + + // 3. 如果 nvps 中有参数,手动构建查询字符串 + // 注意:某些服务器(如接口测试工具)可能期望参数值中的特殊字符(如 == 和 ;)不被编码 + // 因此我们只对参数名进行编码,参数值保持原样或只编码必要的字符 + if (!nvps.isEmpty()) { + StringBuilder queryBuilder = new StringBuilder(); + for (int i = 0; i < nvps.size(); i++) { + NameValuePair nvp = nvps.get(i); + if (i > 0) { + queryBuilder.append("&"); + } + try { + // 只对参数名进行编码 + String encodedName = encodeQueryParam(nvp.getName(), config.inenc()); + // 参数值保持原样,不进行编码(与接口测试工具行为一致) + // 如果参数值中包含需要编码的字符(如中文、空格等),可以在这里添加编码逻辑 + String paramValue = nvp.getValue(); + queryBuilder.append(encodedName).append("=").append(paramValue); + } catch (Exception ex) { + // 如果编码失败,使用原始值 + queryBuilder.append(nvp.getName()).append("=").append(nvp.getValue()); + } + } + String query = queryBuilder.toString(); + if (url.contains("?")) { + url = url + "&" + query; + } else { + url = url + "?" + query; + } + } +// System.out.println(url); + HttpGet httpGet = new HttpGet(url); + setHeaders(httpGet, headers); + setRequestConfig(httpGet, config); + + // 如果配置了 context,使用 context 执行请求以支持 Cookie + // 注意:execute 方法在 HttpClient 5 中被标记为 deprecated,但这是目前推荐的方式 + CloseableHttpResponse response; + if (config.context() != null) { + @SuppressWarnings("deprecation") + CloseableHttpResponse tempResponse = (CloseableHttpResponse) config.client().execute(httpGet, config.context()); + response = tempResponse; + } else { + @SuppressWarnings("deprecation") + CloseableHttpResponse tempResponse = (CloseableHttpResponse) config.client().execute(httpGet); + response = tempResponse; + } + try (response) { + return extractResponseBody(response, config); + } + } + + /** + * 执行 POST 请求(内部方法) + */ + private static String executePost(HttpRequestOptions config, Header[] headers) throws Exception { + String url = config.url(); + List nvps = new ArrayList<>(); + url = HttpEntityBuilder.checkHasParas(url, nvps, config.inenc()); + + HttpPost httpPost = new HttpPost(url); + setHeaders(httpPost, headers); + setRequestConfig(httpPost, config); + + // 设置请求体 + Map map = config.map(); + if (map != null && !map.isEmpty()) { + HttpEntity entity = HttpEntityBuilder.map2HttpEntity(nvps, map, config.inenc()); + httpPost.setEntity(entity); + } + + // 如果配置了 context,使用 context 执行请求以支持 Cookie + // 注意:execute 方法在 HttpClient 5 中被标记为 deprecated,但这是目前推荐的方式 + CloseableHttpResponse response; + if (config.context() != null) { + @SuppressWarnings("deprecation") + CloseableHttpResponse tempResponse = (CloseableHttpResponse) config.client().execute(httpPost, config.context()); + response = tempResponse; + } else { + @SuppressWarnings("deprecation") + CloseableHttpResponse tempResponse = (CloseableHttpResponse) config.client().execute(httpPost); + response = tempResponse; + } + try (response) { + return extractResponseBody(response, config); + } + } + + /** + * 执行 POST 请求(内部方法) + */ + private static HttpResult executePostRes(HttpRequestOptions config, Header[] headers) throws Exception { + String url = config.url(); + List nvps = new ArrayList<>(); + url = HttpEntityBuilder.checkHasParas(url, nvps, config.inenc()); + + HttpPost httpPost = new HttpPost(url); + setHeaders(httpPost, headers); + setRequestConfig(httpPost, config); + + // 设置请求体 + Map map = config.map(); + if (map != null && !map.isEmpty()) { + HttpEntity entity = HttpEntityBuilder.map2HttpEntity(nvps, map, config.inenc()); + httpPost.setEntity(entity); + } + + // 如果配置了 context,使用 context 执行请求以支持 Cookie + // 注意:execute 方法在 HttpClient 5 中被标记为 deprecated,但这是目前推荐的方式 + CloseableHttpResponse response; + if (config.context() != null) { + @SuppressWarnings("deprecation") + CloseableHttpResponse tempResponse = (CloseableHttpResponse) config.client().execute(httpPost, config.context()); + response = tempResponse; + } else { + @SuppressWarnings("deprecation") + CloseableHttpResponse tempResponse = (CloseableHttpResponse) config.client().execute(httpPost); + response = tempResponse; + } + try (response) { + return buildHttpResult(response, config); + } + } + + /** + * 执行 PATCH 请求(内部方法) + */ + private static HttpResult executePatchRes(HttpRequestOptions config, Header[] headers) throws Exception { + String url = config.url(); + List nvps = new ArrayList<>(); + url = HttpEntityBuilder.checkHasParas(url, nvps, config.inenc()); + + HttpPatch httpPatch = new HttpPatch(url); + setHeaders(httpPatch, headers); + setRequestConfig(httpPatch, config); + + // 设置请求体 + Map map = config.map(); + if (map != null && !map.isEmpty()) { + HttpEntity entity = HttpEntityBuilder.map2HttpEntity(nvps, map, config.inenc()); + httpPatch.setEntity(entity); + } + + // 如果配置了 context,使用 context 执行请求以支持 Cookie + // 注意:execute 方法在 HttpClient 5 中被标记为 deprecated,但这是目前推荐的方式 + CloseableHttpResponse response; + if (config.context() != null) { + @SuppressWarnings("deprecation") + CloseableHttpResponse tempResponse = (CloseableHttpResponse) config.client().execute(httpPatch, config.context()); + response = tempResponse; + } else { + @SuppressWarnings("deprecation") + CloseableHttpResponse tempResponse = (CloseableHttpResponse) config.client().execute(httpPatch); + response = tempResponse; + } + try (response) { + return buildHttpResult(response, config); + } + } + + /** + * 执行通用请求(根据 HttpMethods 自动选择)(内部方法) + */ + private static String execute(HttpRequestOptions config, Header[] headers) throws Exception { + HttpMethods method = config.method(); + String url = config.url(); + List nvps = new ArrayList<>(); + url = HttpEntityBuilder.checkHasParas(url, nvps, config.inenc()); + + ClassicHttpRequest request = createRequest(method, url); + setHeaders(request, headers); + setRequestConfig(request, config); + + // 设置请求体(仅 POST 和 PUT) + Map map = config.map(); + if (map != null && !map.isEmpty() && (request instanceof HttpPost || request instanceof HttpPut)) { + HttpEntity entity = HttpEntityBuilder.map2HttpEntity(nvps, map, config.inenc()); + if (request instanceof HttpPost) { + ((HttpPost) request).setEntity(entity); + } else if (request instanceof HttpPut) { + ((HttpPut) request).setEntity(entity); + } + } + + // 如果配置了 context,使用 context 执行请求以支持 Cookie + // 注意:execute 方法在 HttpClient 5 中被标记为 deprecated,但这是目前推荐的方式 + CloseableHttpResponse response; + if (config.context() != null) { + @SuppressWarnings("deprecation") + CloseableHttpResponse tempResponse = (CloseableHttpResponse) config.client().execute(request, config.context()); + response = tempResponse; + } else { + @SuppressWarnings("deprecation") + CloseableHttpResponse tempResponse = (CloseableHttpResponse) config.client().execute(request); + response = tempResponse; + } + try (response) { + return extractResponseBody(response, config); + } + } + + /** + * 执行请求并返回详细响应信息(内部方法) + */ + private static HttpResult executeAndGetResp(HttpRequestOptions config, Header[] headers) throws Exception { + String url = config.url(); + List nvps = new ArrayList<>(); + // 1. 先解析 URL 中原本就带的查询参数(如果有) + url = HttpEntityBuilder.checkHasParas(url, nvps, config.inenc()); + + // 2. 再把 HttpConfig.map() 里的参数也加入到 nvps 中 + // 对于 GET 请求,这些参数应该拼到 URL 上,而不是放到 body 里 + Map map = config.map(); + if (map != null && !map.isEmpty()) { + // 这里主要是为了复用参数编码逻辑,返回的 entity 我们不用 + HttpEntityBuilder.map2HttpEntity(nvps, map, config.inenc()); + } + + // 3. 如果 nvps 中有参数,手动构建查询字符串 + // 注意:某些服务器(如接口测试工具)可能期望参数值中的特殊字符(如 == 和 ;)不被编码 + // 因此我们只对参数名进行编码,参数值保持原样或只编码必要的字符 + if (!nvps.isEmpty()) { + StringBuilder queryBuilder = new StringBuilder(); + for (int i = 0; i < nvps.size(); i++) { + NameValuePair nvp = nvps.get(i); + if (i > 0) { + queryBuilder.append("&"); + } + try { + // 只对参数名进行编码 + String encodedName = encodeQueryParam(nvp.getName(), config.inenc()); + // 参数值保持原样,不进行编码(与接口测试工具行为一致) + // 如果参数值中包含需要编码的字符(如中文、空格等),可以在这里添加编码逻辑 + String paramValue = nvp.getValue(); + queryBuilder.append(encodedName).append("=").append(paramValue); + } catch (Exception ex) { + // 如果编码失败,使用原始值 + queryBuilder.append(nvp.getName()).append("=").append(nvp.getValue()); + } + } + String query = queryBuilder.toString(); + if (url.contains("?")) { + url = url + "&" + query; + } else { + url = url + "?" + query; + } + } +// System.out.println(url); + HttpGet httpGet = new HttpGet(url); + setHeaders(httpGet, headers); + setRequestConfig(httpGet, config); + + // 如果配置了 context,使用 context 执行请求以支持 Cookie + // 注意:execute 方法在 HttpClient 5 中被标记为 deprecated,但这是目前推荐的方式 + CloseableHttpResponse response; + if (config.context() != null) { + @SuppressWarnings("deprecation") + CloseableHttpResponse tempResponse = (CloseableHttpResponse) config.client().execute(httpGet, config.context()); + response = tempResponse; + } else { + @SuppressWarnings("deprecation") + CloseableHttpResponse tempResponse = (CloseableHttpResponse) config.client().execute(httpGet); + response = tempResponse; + } + try (response) { + return buildHttpResult(response, config); + } + } + + /** + * 创建 HTTP 请求对象 + */ + private static ClassicHttpRequest createRequest(HttpMethods method, String url) { + switch (method) { + case GET: + return new HttpGet(url); + case POST: + return new HttpPost(url); + case PUT: + return new HttpPut(url); + case DELETE: + return new HttpDelete(url); + default: + return new HttpGet(url); + } + } + + /** + * 设置请求头 + */ + private static void setHeaders(ClassicHttpRequest request, Header[] headers) { + if (headers != null) { + for (Header header : headers) { + request.addHeader(header.getName(), header.getValue()); + } + } + } + + /** + * 设置请求配置 + */ + private static void setRequestConfig(ClassicHttpRequest request, HttpRequestOptions config) { + if (config.requestConfig() == null) { + return; + } + + if (request instanceof HttpGet) { + ((HttpGet) request).setConfig(config.requestConfig()); + } else if (request instanceof HttpPost) { + ((HttpPost) request).setConfig(config.requestConfig()); + } else if (request instanceof HttpPut) { + ((HttpPut) request).setConfig(config.requestConfig()); + } else if (request instanceof HttpPatch) { + ((HttpPatch) request).setConfig(config.requestConfig()); + } else if (request instanceof HttpDelete) { + ((HttpDelete) request).setConfig(config.requestConfig()); + } + } + + /** + * 提取响应体内容 + */ + private static String extractResponseBody(CloseableHttpResponse response, HttpRequestOptions config) throws Exception { + HttpEntity entity = response.getEntity(); + if (entity == null) { + return ""; + } + try { + String result = EntityUtils.toString(entity, Charset.forName(config.outenc())); + return result; + } finally { + EntityUtils.consume(entity); + } + } + + /** + * 构建 HttpResult 对象 + */ + private static HttpResult buildHttpResult(CloseableHttpResponse response, HttpRequestOptions config) throws Exception { + // 直接使用 HttpResult 构造函数,自动设置状态行、响应头、协议版本和状态码 + HttpResult result = new HttpResult(response); + + // 读取响应体内容 + HttpEntity entity = response.getEntity(); + if (entity != null) { + String responseBody = EntityUtils.toString(entity, Charset.forName(config.outenc())); + result.setResult(responseBody); + EntityUtils.consume(entity); + } + + return result; + } + + /** + * 编码查询参数名(只编码参数名,不编码参数值) + * 这样可以保持与接口测试工具的行为一致 + * + * @param param 参数名 + * @param encoding 编码格式 + * @return 编码后的参数名 + */ + private static String encodeQueryParam(String param, String encoding) { + if (param == null) { + return ""; + } + try { + // 只对参数名进行标准 URL 编码 + return java.net.URLEncoder.encode(param, encoding).replace("+", "%20"); + } catch (java.io.UnsupportedEncodingException e) { + return param; + } + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpEntityBuilder.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpEntityBuilder.java new file mode 100644 index 0000000..b899070 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpEntityBuilder.java @@ -0,0 +1,310 @@ +package com.mgm.mcrm.apigateway.daylight.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/daylight/common/httpclient/HttpRequestOptions.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpRequestOptions.java new file mode 100644 index 0000000..68dea0b --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpRequestOptions.java @@ -0,0 +1,375 @@ +package com.mgm.mcrm.apigateway.daylight.common.httpclient; + +import com.mgm.mcrm.apigateway.daylight.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/daylight/common/httpclient/HttpSslSupport.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpSslSupport.java new file mode 100644 index 0000000..3500bdc --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/httpclient/HttpSslSupport.java @@ -0,0 +1,197 @@ +package com.mgm.mcrm.apigateway.daylight.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/daylight/common/scheduler/DaylightConfigurationScheduler.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/scheduler/DaylightConfigurationScheduler.java index d5eea26..9050903 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/scheduler/DaylightConfigurationScheduler.java +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/scheduler/DaylightConfigurationScheduler.java @@ -4,6 +4,7 @@ import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; import com.mgm.mcrm.apigateway.daylight.common.services.DaylightFetchConfigurationService; import com.mgm.mcrm.apigateway.common.utils.LogUtil; import com.mgm.mcrm.apigateway.daylight.repositories.CfgDaylightConfigurationDao; +import com.mgm.mcrm.apigateway.daylight.services.DaylightApiService; import jakarta.annotation.Resource; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; @@ -18,6 +19,8 @@ public class DaylightConfigurationScheduler { private DaylightFetchConfigurationService daylightFetchConfigurationService; @Resource private CfgDaylightConfigurationDao cfgDaylightConfigurationDao; + @Resource + private DaylightApiService daylightApiService; @Order(1) @EventListener(ApplicationReadyEvent.class) @@ -30,7 +33,7 @@ public class DaylightConfigurationScheduler { } LogUtil.warn(DaylightConfigurationScheduler.class, "No saved Daylight properties found, start startup fetch"); - BaseApiResponse propResponse = daylightFetchConfigurationService.fetchDaylightProperties(); + BaseApiResponse propResponse = daylightFetchConfigurationService.fetchDaylightProperties(); if (propResponse.isSuccess()) { LogUtil.info(DaylightConfigurationScheduler.class, "Startup properties completed: " + propResponse.getMessage()); } else { @@ -52,7 +55,7 @@ public class DaylightConfigurationScheduler { } LogUtil.warn(DaylightConfigurationScheduler.class, "No saved Daylight configurations found, start startup fetch"); - BaseApiResponse configResponse = daylightFetchConfigurationService.fetchDaylightConfiguration(); + BaseApiResponse configResponse = daylightFetchConfigurationService.fetchDaylightConfiguration(); if (configResponse.isSuccess()) { LogUtil.info(DaylightConfigurationScheduler.class, "Startup configurations completed: " + configResponse.getMessage()); } else { @@ -79,7 +82,7 @@ public class DaylightConfigurationScheduler { } LogUtil.warn(DaylightConfigurationScheduler.class, "No saved Daylight block codes found, start startup fetch"); - BaseApiResponse blockResponse = daylightFetchConfigurationService.fetchDaylightBlockCodes(); + BaseApiResponse blockResponse = daylightFetchConfigurationService.fetchDaylightBlockCodes(); if (blockResponse.isSuccess()) { LogUtil.info(DaylightConfigurationScheduler.class, "Startup block codes completed: " + blockResponse.getMessage()); } else { @@ -99,7 +102,7 @@ public class DaylightConfigurationScheduler { try { // 1. 拉取 Properties LogUtil.info(DaylightConfigurationScheduler.class, "Step 1/3: Fetching properties..."); - BaseApiResponse propResponse = daylightFetchConfigurationService.fetchDaylightProperties(); + BaseApiResponse propResponse = daylightFetchConfigurationService.fetchDaylightProperties(); if (propResponse.isSuccess()) { LogUtil.info(DaylightConfigurationScheduler.class, "Properties completed: " + propResponse.getMessage()); } else { @@ -108,7 +111,7 @@ public class DaylightConfigurationScheduler { // 2. 拉取 Configuration LogUtil.info(DaylightConfigurationScheduler.class, "Step 2/3: Fetching configurations..."); - BaseApiResponse configResponse = daylightFetchConfigurationService.fetchDaylightConfiguration(); + BaseApiResponse configResponse = daylightFetchConfigurationService.fetchDaylightConfiguration(); if (configResponse.isSuccess()) { LogUtil.info(DaylightConfigurationScheduler.class, "Configurations completed: " + configResponse.getMessage()); } else { @@ -117,7 +120,7 @@ public class DaylightConfigurationScheduler { // 3. 拉取 Block Codes LogUtil.info(DaylightConfigurationScheduler.class, "Step 3/3: Fetching block codes..."); - BaseApiResponse blockResponse = daylightFetchConfigurationService.fetchDaylightBlockCodes(); + BaseApiResponse blockResponse = daylightFetchConfigurationService.fetchDaylightBlockCodes(); if (blockResponse.isSuccess()) { LogUtil.info(DaylightConfigurationScheduler.class, "Block codes completed: " + blockResponse.getMessage()); } else { diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightFetchConfigurationService.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightFetchConfigurationService.java index a854f90..00a1e8f 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightFetchConfigurationService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightFetchConfigurationService.java @@ -1,6 +1,7 @@ package com.mgm.mcrm.apigateway.daylight.common.services; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.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; @@ -51,8 +52,8 @@ public class DaylightFetchConfigurationService { @Resource private SysLogDaylightConfigurationSyncDao syncLogDao; - public BaseApiResponse fetchDaylightConfiguration() { - BaseApiResponse response = new BaseApiResponse(); + public BaseApiResponse fetchDaylightConfiguration() { + BaseApiResponse response = new BaseApiResponse(); Date syncStartTime = new Date(); try { @@ -185,7 +186,7 @@ public class DaylightFetchConfigurationService { .findByDlPropertyIdAndConfigName(dlPropertyId, configType); if (existing != null && StringUtils.isNotBlank(existing.getConfigValue())) { // 解析旧数据,用 id 建索引 - List oldItems = JSONObject.parseArray(existing.getConfigValue(), JSONObject.class); + List oldItems = JSON.parseArray(existing.getConfigValue(), JSONObject.class); Map mergedMap = new HashMap<>(); for (JSONObject item : oldItems) { mergedMap.put(getConfigItemKey(item), item); @@ -217,7 +218,7 @@ public class DaylightFetchConfigurationService { CfgDaylightConfiguration existing = cfgDaylightConfigurationDao .findByDlPropertyIdAndConfigName(dlPropertyId, configType); List oldItems = existing != null && StringUtils.isNotBlank(existing.getConfigValue()) - ? JSONObject.parseArray(existing.getConfigValue(), JSONObject.class) + ? JSON.parseArray(existing.getConfigValue(), JSONObject.class) : new ArrayList<>(); CfgDaylightConfiguration config = existing != null ? existing : new CfgDaylightConfiguration(); config.setConfigName(configType); @@ -256,8 +257,8 @@ public class DaylightFetchConfigurationService { /** * 从 Daylight API 拉取 properties 列表,存入 cfg_daylight_configuration 表 */ - public BaseApiResponse fetchDaylightProperties() { - BaseApiResponse response = new BaseApiResponse(); + public BaseApiResponse fetchDaylightProperties() { + BaseApiResponse response = new BaseApiResponse<>(); Date syncStartTime = new Date(); try { @@ -339,7 +340,7 @@ public class DaylightFetchConfigurationService { CfgDaylightConfiguration config = cfgDaylightConfigurationDao .findByDlPropertyIdAndConfigName(null, "Property"); List oldItems = config != null && StringUtils.isNotBlank(config.getConfigValue()) - ? JSONObject.parseArray(config.getConfigValue(), JSONObject.class) + ? JSON.parseArray(config.getConfigValue(), JSONObject.class) : new ArrayList<>(); if (config == null) { config = new CfgDaylightConfiguration(); @@ -481,8 +482,8 @@ public class DaylightFetchConfigurationService { /** * 从 Daylight API 拉取 block codes 列表,按 property 遍历,存入 cfg_daylight_configuration 表 */ - public BaseApiResponse fetchDaylightBlockCodes() { - BaseApiResponse response = new BaseApiResponse(); + public BaseApiResponse fetchDaylightBlockCodes() { + BaseApiResponse response = new BaseApiResponse<>(); Date syncStartTime = new Date(); String dlPropertyId = null; @@ -532,6 +533,7 @@ public class DaylightFetchConfigurationService { String path = "/api-gateway/data-api/v1/blocks"; String apiUrl = daylightApiSetting.getServerUrl() + path; List allResults = new ArrayList<>(); + boolean fetchSuccess = true; HttpHeaders headers = new HttpHeaders(); headers.set("authorization", "Bearer " + daylightApiToken.getAccessToken()); @@ -552,38 +554,37 @@ public class DaylightFetchConfigurationService { ).block(); if (requestResponse == null) { - response.setSuccess(false); - response.setMessage("Http request server can not visit."); - return response; - } + log.warn("Property {} BlockCode fetch failed: Http request server can not visit", dlPropertyId); + fetchSuccess = false; + } else { + response.setStatusCode(requestResponse.getStatusCode().value()); - 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 {} BlockCode fetch failed: {}", dlPropertyId, errorMsg); + fetchSuccess = false; + } else { + byte[] responseBody = requestResponse.getBody(); + if (responseBody != null) { + String jsonStr = new String(responseBody, StandardCharsets.UTF_8); + JSONObject responseJson = JSONObject.parseObject(jsonStr); - 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); + List pageResults = responseJson.getJSONArray("results").toJavaList(JSONObject.class); + if (pageResults != null) { + allResults.addAll(pageResults); + } + } } } // 保存为一条 CfgDaylightConfiguration,configName = "BlockCode" - if (!allResults.isEmpty()) { + if (fetchSuccess && !allResults.isEmpty()) { boolean isIncremental = StringUtils.isNotBlank(updatedAtParam); if (isIncremental && existingConfig != null && StringUtils.isNotBlank(existingConfig.getConfigValue())) { // 增量同步:按 id 合并新旧数据 - List oldItems = JSONObject.parseArray(existingConfig.getConfigValue(), JSONObject.class); + List oldItems = JSON.parseArray(existingConfig.getConfigValue(), JSONObject.class); Map mergedMap = new HashMap<>(); for (JSONObject item : oldItems) { mergedMap.put(getConfigItemKey(item), item); @@ -601,7 +602,7 @@ public class DaylightFetchConfigurationService { // 全量同步:直接保存 List oldItems = existingConfig != null && StringUtils.isNotBlank(existingConfig.getConfigValue()) - ? JSONObject.parseArray(existingConfig.getConfigValue(), JSONObject.class) + ? JSON.parseArray(existingConfig.getConfigValue(), JSONObject.class) : new ArrayList<>(); CfgDaylightConfiguration config = existingConfig != null ? existingConfig : new CfgDaylightConfiguration(); if (config.getConfigName() == null) { @@ -618,20 +619,20 @@ public class DaylightFetchConfigurationService { } savedCount++; } + + // 按 property 记录同步日志 + SysLogDaylightConfigurationSync syncLog = new SysLogDaylightConfigurationSync(); + syncLog.setConfigName("BlockCode"); + syncLog.setDlPropertyId(dlPropertyId); + syncLog.setLastSyncAt(syncStartTime); + syncLog.setSyncStatus(fetchSuccess ? "SUCCESS" : "FAILED"); + syncLog.setRecordCount(allResults.size()); + syncLog.setCreatedAt(new Date()); + syncLogDao.save(syncLog); } 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()); diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightReservationService.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightReservationService.java new file mode 100644 index 0000000..51c5fa3 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/DaylightReservationService.java @@ -0,0 +1,88 @@ +package com.mgm.mcrm.apigateway.daylight.common.services; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.mgm.mcrm.apigateway.common.dtos.config.DaylightApiSetting; +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.ResponseEntity; +import org.springframework.stereotype.Service; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; + +@Service +public class DaylightReservationService { + private final Logger logger = LoggerFactory.getLogger(DaylightReservationService.class); + private final HttpProxyErrorHandler httpProxyErrorHandler = new HttpProxyErrorHandler(); + @Resource + private WebProxyService webProxyService; + @Autowired + private DaylightApiSetting daylightApiSetting; + @Resource + private DaylightTokenService daylightTokenService; + + public JSONObject getDaylightReservationByDlId( + String dlReservationId, + String dlPropertyId + ) { + logger.info("Entering getDaylightReservationByDlId with dlReservationId: {}, dlPropertyId: {}", dlReservationId, dlPropertyId); + try { + TokenResponse daylightApiToken = daylightTokenService.getApiAccessToken(); + if (daylightApiToken == null || StringUtils.isBlank(daylightApiToken.getAccessToken())) { + logger.error("Failed to obtain Daylight API access token."); + throw new RuntimeException("Invoke daylight token failed"); + } + + String apiUrl = daylightApiSetting.getServerUrl() + "/api-gateway/booking/v1/reservations/" + dlReservationId; + String queryString = "extend=PurchaseElements,CustomFields"; + + HttpHeaders headers = new HttpHeaders(); + headers.set("authorization", "Bearer " + daylightApiToken.getAccessToken()); + headers.set("AC-Property-Id", dlPropertyId); + headers.set("Content-Type", "application/json"); + + logger.info("Invoking Daylight API: {} with query: {}", apiUrl, queryString); + ResponseEntity requestResponse = webProxyService.invokeRequest( + WebProxyService.DAYLIGHT_API, + HttpMethod.GET, + apiUrl, + queryString, + headers, + new byte[0], + Duration.ofSeconds(daylightApiSetting.getApiTimeout()), + httpProxyErrorHandler + ).block(); + + if (requestResponse != null) { + logger.info("Daylight API response status: {}", requestResponse.getStatusCode()); + if (requestResponse.getBody() != null) { + String responseData = new String(requestResponse.getBody(), StandardCharsets.UTF_8); + logger.info("Successfully retrieved daylight reservation data."); + JSONObject response = JSONObject.parseObject(responseData); + JSONArray reservations = response.getJSONArray("results"); + JSONObject reservation = reservations.getJSONObject(0); + return reservation; + } else { + logger.warn("Daylight API returned an empty body for reservation: {}", dlReservationId); + } + } else { + logger.error("Daylight API request returned null for reservation: {}", dlReservationId); + } + } catch (Exception e) { + logger.error("Error occurred while fetching daylight reservation by ID: {}", dlReservationId, e); + throw new RuntimeException(e); + } + + return null; + } +} 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 index f754355..ca36384 100644 --- 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 @@ -1,6 +1,6 @@ package com.mgm.mcrm.apigateway.daylight.common.services; -import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson2.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; diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/HttpService.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/HttpService.java new file mode 100644 index 0000000..fc9cd61 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/common/services/HttpService.java @@ -0,0 +1,209 @@ +package com.mgm.mcrm.apigateway.daylight.common.services; + +import com.alibaba.fastjson2.JSON; +import com.mgm.mcrm.apigateway.daylight.common.dtos.HttpResult; +import com.mgm.mcrm.apigateway.daylight.common.enums.HttpMethods; +import com.mgm.mcrm.apigateway.daylight.common.httpclient.HttpClientExecutor; +import com.mgm.mcrm.apigateway.daylight.common.httpclient.HttpRequestOptions; +import jakarta.annotation.Resource; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.core5.http.Header; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 請求服務 + * + * @author Aaka + */ +@Service +public class HttpService { + + private static final Logger log = LoggerFactory.getLogger(HttpService.class); + + + @Resource + @Qualifier("httpClientWithSSL") + private CloseableHttpClient httpClient; + + @Resource + @Qualifier("httpRequestConfig") + private RequestConfig requestConfig; + + @Value("${daylight.url}") + private String daylightUrl; + + public String sslGet(String url, Header[] headers, Map map) throws Exception { + + log.info("======sslGet======url:{}===========headers:{} =============== map:{}", daylightUrl + url, JSON.toJSONString(headers), JSON.toJSONString(map)); + // 使用 HttpConfig 配置 GET 请求(带参数) + HttpRequestOptions getConfig = HttpRequestOptions.create() + .url(daylightUrl + url) + .timeout(requestConfig) + .map(map) + .encoding("utf-8") + .client(httpClient); + + String result = HttpClientExecutor.get(getConfig, headers); + log.info("====== GET request result size:{}", result.length()); + + return result; + } + + public String sslGetNoLog(String url, Header[] headers, Map map) throws Exception { + + // todo 在這裏需要把請求頭的信息插入到數據庫裏 + log.info("======sslGet======url:{}===========headers:{} =============== map:{}", daylightUrl + url, JSON.toJSONString(headers), JSON.toJSONString(map)); + // 使用 HttpConfig 配置 GET 请求(带参数) + HttpRequestOptions getConfig = HttpRequestOptions.create() + .url(daylightUrl + url) + .timeout(requestConfig) + .map(map) + .encoding("utf-8") + .client(httpClient); + + String result = HttpClientExecutor.get(getConfig, headers); + log.info("====== GET request result size={}", result.length()); + + return result; + } + + public HttpResult sslGetHttpResult(String url, Header[] headers, Map map) throws Exception { + + // todo 在這裏需要把請求頭的信息插入到數據庫裏 + log.info("======sslGet======url:{}===========headers:{} =============== map:{}", daylightUrl + url, JSON.toJSONString(headers), JSON.toJSONString(map)); + // 使用 HttpConfig 配置 GET 请求(带参数) + HttpRequestOptions getConfig = HttpRequestOptions.create() + .url(daylightUrl + url) + .timeout(requestConfig) + .map(map) + .encoding("utf-8") + .client(httpClient); + + HttpResult result = HttpClientExecutor.sendAndGetResp(getConfig, headers); + log.info("====== GET request result:{}", result.getResult()); + + return result; + } + + /** + * 使用 HttpConfig 配置 PUT 请求 + * + * @param url 請求路徑 + * @param headers 請求頭 + * @param map 請求參數 + * @return 接口結果的json數據 + * @throws Exception 異常 + */ + public String sslPut(String url, Header[] headers, Map map) throws Exception { + log.info("======sslPut======url:{}===========headers:{} =============== map:{}", daylightUrl + url, JSON.toJSONString(headers), JSON.toJSONString(map)); + HttpRequestOptions putConfig = HttpRequestOptions.create() + .url(daylightUrl + url) + .timeout(requestConfig) + .method(HttpMethods.PUT) + .json(JSON.toJSONString(map)) + .encoding("utf-8") + .client(httpClient); + String result = HttpClientExecutor.send(putConfig, headers); + log.info("====== PUT request result:{}", result); + + return result; + } + + /** + * 使用 HttpConfig 配置 Delete 请求 + * + * @param url 請求路徑 + * @param headers 請求頭 + * @param requestBody 請求參數 + * @return 接口結果的json數據 + * @throws Exception 異常 + */ + public String sslDelete(String url, Header[] headers, Object requestBody) throws Exception { + log.info("======sslDelete======url:{}===========headers:{} =============== map:{}", daylightUrl + url, JSON.toJSONString(headers), JSON.toJSONString(requestBody)); + HttpRequestOptions deleteConfig = HttpRequestOptions.create() + .url(daylightUrl + url) + .timeout(requestConfig) + .json(JSON.toJSONString(requestBody)) + .method(HttpMethods.DELETE) + .client(httpClient); + String result = HttpClientExecutor.send(deleteConfig, headers); + log.info("====== DELETE request result:{}", result); + return result; + } + + /** + * 使用 HttpConfig 配置 Post 请求 + * + * @param url 請求路徑 + * @param headers 請求頭 + * @param requestBody request 数据 + * @return 接口結果的json數據 + * @throws Exception 異常 + */ + public HttpResult sslPost(String url, Header[] headers, Object requestBody) throws Exception { + log.info("======sslCreatePost======url:{}===========headers:{}", daylightUrl + url, JSON.toJSONString(headers)); + HttpRequestOptions postConfig = HttpRequestOptions.create() + .url(daylightUrl + url) + .timeout(requestConfig) + .json(JSON.toJSONString(requestBody)) + .method(HttpMethods.POST) + .client(httpClient); + HttpResult result = HttpClientExecutor.sendAndPostResp(postConfig, headers); +// System.out.println("POST 请求结果:\n" + result); + log.info("====== POST request result:{}", result); + return result; + } + + /** + * 使用 HttpConfig 配置 PATCH 请求(JSON 请求体) + * + * @param url 請求路徑 + * @param headers 請求頭 + * @param requestBody request 数据 + * @return 接口結果的json數據 + * @throws Exception 異常 + */ + public HttpResult sslPatch(String url, Header[] headers, Object requestBody) throws Exception { + log.info("======sslCreatePatch======url:{}===========headers:{}", daylightUrl + url, JSON.toJSONString(headers)); + HttpRequestOptions patchConfig = HttpRequestOptions.create() + .url(daylightUrl + url) + .timeout(requestConfig) + .json(JSON.toJSONString(requestBody)) + .method(HttpMethods.PATCH) + .encoding("utf-8") + .client(httpClient); + HttpResult result = HttpClientExecutor.sendAndPatchResp(patchConfig, headers); + log.info("====== PATCH request result:{}", result); + return result; + } + + /** + * 使用 HttpConfig 配置 Delete 请求 + * + * @param url 請求路徑 + * @param headers 請求頭 + * @param map Cancel数据 + * @return 接口結果的json數據 + * @throws Exception 異常 + */ + public HttpResult sslCancelPost(String url, Header[] headers, Map map) throws Exception { + log.info("======sslCancelPost======url:{}===========headers:{}", daylightUrl + url, JSON.toJSONString(headers)); + HttpRequestOptions postConfig = HttpRequestOptions.create() + .url(daylightUrl + url) + .timeout(requestConfig) + .json(JSON.toJSONString(map)) + .method(HttpMethods.POST) + .client(httpClient); + HttpResult result = HttpClientExecutor.sendAndPostResp(postConfig, headers); +// System.out.println("POST 请求结果:\n" + result); + log.info("====== POST request result:{}", result); + return result; + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/dtos/response/CompRuleResponse.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/dtos/response/CompRuleResponse.java new file mode 100644 index 0000000..72f34ef --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/dtos/response/CompRuleResponse.java @@ -0,0 +1,61 @@ +package com.mgm.mcrm.apigateway.daylight.dtos.response; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +public class CompRuleResponse { + + private String id; + private Integer version; + private String accountId; + private String profileId; + private String folioWindowId; + private String complimentaryTypeId; + private List complimentaryAccountingPresetsIds; + private String requestorId; + private String approverId; + private String paymentMethodTransactionCodeId; + private String justification; + private ComplimentaryRuleLimits complimentaryRuleLimits; + private List settledFolioWindowIds; + private Boolean isExecutive; + private Boolean doNotIssueInvoice; + private Boolean isActive; + private Boolean canBeDeleted; + private Integer issuedVouchersCount; + + @Data + public static class ComplimentaryRuleLimits { + + private Amount totalAmountLimit; + private SoftTotalLimit softTotalLimit; + private List limitExcludedPresetIds; + private List limitExcludedTransactionCodeIds; + private TotalLimit totalLimit; + + @Data + public static class SoftTotalLimit { + + private Amount amountLimit; + private Amount remainingLimit; + private Boolean isExceeded; + } + + @Data + public static class TotalLimit { + + private Amount amountLimit; + private Amount remainingLimit; + } + + @Data + public static class Amount { + + private BigDecimal amount; + private String currency; + } + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/models/CfgCompRule.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/models/CfgCompRule.java new file mode 100644 index 0000000..ccff8e5 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/models/CfgCompRule.java @@ -0,0 +1,39 @@ +package com.mgm.mcrm.apigateway.daylight.models; + +import jakarta.persistence.*; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +@Data +@Entity +@Table(name = "cfg_comp_rule") +public class CfgCompRule implements Serializable { + + @Id + @Column(name = "id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "dl_reservation_id") + private String dlReservationId; + + @Column(name = "comp_rule_id", nullable = false, unique = true) + private String compRuleId; + + @Column(name = "account_id") + private String accountId; + + @Column(name = "is_send_soft_limit", nullable = false) + private Integer isSendSoftLimit = 0; + + @Column(name = "is_send_total_limit", nullable = false) + private Integer isSendTotalLimit = 0; + + @Column(name = "created_at") + private LocalDateTime createdAt; + + @Column(name = "updated_at") + private LocalDateTime updatedAt; +} \ No newline at end of file diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/CfgCompRuleDao.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/CfgCompRuleDao.java new file mode 100644 index 0000000..b8e2f00 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/repositories/CfgCompRuleDao.java @@ -0,0 +1,10 @@ +package com.mgm.mcrm.apigateway.daylight.repositories; + +import com.mgm.mcrm.apigateway.daylight.models.CfgCompRule; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface CfgCompRuleDao extends JpaRepository { + CfgCompRule findByCompRuleId(String compRuleId); +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/services/DaylightApiService.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/services/DaylightApiService.java new file mode 100644 index 0000000..4fba1ac --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/services/DaylightApiService.java @@ -0,0 +1,84 @@ +package com.mgm.mcrm.apigateway.daylight.services; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; +import com.mgm.mcrm.apigateway.common.httpclient.HttpHeaderBuilder; +import com.mgm.mcrm.apigateway.daylight.common.services.HttpService; +import com.mgm.mcrm.apigateway.daylight.dtos.response.CompRuleResponse; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.hc.core5.http.Header; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Slf4j +@Service +public class DaylightApiService { + + @Resource + private HttpService httpService; + + public BaseApiResponse> daylightGetCompRulesByAccount(String dlPropertyId, String dlAccountId) { + + BaseApiResponse> result = new BaseApiResponse<>(); + + try { + String url = "/api-gateway/cashiering/v1/accounts/" + dlAccountId + "/complimentary-rules"; + // 获取对应的值 + Header[] headers = HttpHeaderBuilder.custom() + .contentType("application/json") + .ac_property_id(dlPropertyId) + .build(); + // 参数设置 + Map map = new HashMap<>(); + + String responseData = httpService.sslGet(url, headers, map); + + List compRuleResponses = JSON.parseArray(responseData, CompRuleResponse.class); + + result.setSuccess(true); + result.setData(compRuleResponses); + + } catch (Exception e) { + result.setSuccess(false); + result.setData(Collections.emptyList()); + result.setMessage("Failed to get dayLight comp rules for account: " + dlAccountId); + log.error("Failed to get dayLight comp rules: ", e); + } + return result; + } + + public BaseApiResponse getDaylightReservationByDlId( + String dlReservationId, + String dlPropertyId + ) { + + BaseApiResponse result = new BaseApiResponse<>(); + + try { + String url = "/api-gateway/booking/v1/reservations/" + dlReservationId + "?extend=PurchaseElements,CustomFields"; + // 获取对应的值 + Header[] headers = HttpHeaderBuilder.custom() + .contentType("application/json") + .ac_property_id(dlPropertyId) + .build(); + // 参数设置 + Map map = new HashMap<>(); + + String responseData = httpService.sslGet(url, headers, map); + + JSONObject jsonObject = JSON.parseObject(responseData); + + result.setSuccess(true); + result.setData(jsonObject); + + } catch (Exception e) { + result.setSuccess(false); + result.setMessage("Failed to get reservation , id=" + dlReservationId); + log.error("Failed to get reservation , id={}", dlReservationId, e); + } + return result; + } +} 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 index cf6616c..5da1c5c 100644 --- 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 @@ -24,12 +24,12 @@ public class DaylightWebhook { private WebhookLogService webhookLogService; @PostMapping(value = "/receive", produces = "application/json;charset=UTF-8") - public ResponseEntity handleRoomAvailableWebhook( + public ResponseEntity> handleRoomAvailableWebhook( @RequestBody(required = true) String payload, @RequestHeader Map headers, HttpServletRequest request ){ - BaseApiResponse result = new BaseApiResponse(); + BaseApiResponse result = new BaseApiResponse<>(); String messageId = request.getHeader("Ac-Events-Message-Id"); String eventType = request.getHeader("Ac-Events-Type"); diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ExceededCompRuleEvent.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ExceededCompRuleEvent.java new file mode 100644 index 0000000..5166118 --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/dtos/reservation/ExceededCompRuleEvent.java @@ -0,0 +1,12 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation; + +import lombok.Data; + +@Data +public class ExceededCompRuleEvent { + private String complimentaryRuleId; + private boolean softTotalLimitIsExceeded; + private boolean softDailyLimitIsExceeded; + private boolean isExecutive; + private String trigger; +} \ No newline at end of file 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 index 3212682..27cf188 100644 --- 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 @@ -1,11 +1,10 @@ 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; + + 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 index 017f367..09813e3 100644 --- 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 @@ -20,8 +20,8 @@ import java.util.Map; public class AvailableRoomCountUpdatedHandler implements DaylightWebhookHandler { private final RoomCriteriaDaylightService roomCriteriaDaylightService; @Override - public BaseApiResponse handle(String payload, Map headers) { - BaseApiResponse result = new BaseApiResponse(); + public BaseApiResponse handle(String payload, Map headers) { + BaseApiResponse result = new BaseApiResponse<>(); AvailableRoomCountUpdatedEvent eventData = null; try{ eventData = JSONObject.parseObject(payload, AvailableRoomCountUpdatedEvent.class); diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/CompRuleLimitsExceededChangedHandler.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/CompRuleLimitsExceededChangedHandler.java new file mode 100644 index 0000000..941326a --- /dev/null +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/CompRuleLimitsExceededChangedHandler.java @@ -0,0 +1,224 @@ +package com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl; + +import com.alibaba.fastjson2.JSONObject; +import com.mgm.mcrm.apigateway.common.dtos.BaseApiResponse; +import com.mgm.mcrm.apigateway.common.utils.LogUtil; +import com.mgm.mcrm.apigateway.common.utils.NotificationUtil; +import com.mgm.mcrm.apigateway.daylight.dtos.response.CompRuleResponse; +import com.mgm.mcrm.apigateway.daylight.models.CfgCompRule; +import com.mgm.mcrm.apigateway.daylight.repositories.CfgCompRuleDao; +import com.mgm.mcrm.apigateway.daylight.services.DaylightApiService; +import com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation.ExceededCompRuleEvent; +import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.DaylightWebhookHandler; +import com.mgm.mcrm.apigateway.logging.dao.SysLogDeviceTokenRepository; +import com.mgm.mcrm.apigateway.logging.models.SysLogDeviceToken; +import com.mgm.mcrm.apigateway.notification.services.ApnNotificationService; +import com.mgm.mcrm.apigateway.reservation.models.Bookings; +import com.mgm.mcrm.apigateway.reservation.repositories.BookingDao; +import com.mgm.mcrm.apigateway.reservation.services.BookingService; +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +@Slf4j +@Component +@RequiredArgsConstructor +public class CompRuleLimitsExceededChangedHandler implements DaylightWebhookHandler { + + @Resource + private CfgCompRuleDao cfgCompRuleDao; + @Resource + private BookingDao bookingDao; + @Resource + private SysLogDeviceTokenRepository sysLogDeviceTokenRepository; + @Resource + private NotificationUtil notificationUtil; + @Resource + private ApnNotificationService apnNotificationService; + + @Resource + private DaylightApiService daylightApiService; + + @Override + public BaseApiResponse handle(String payload, Map headers) { + BaseApiResponse result = new BaseApiResponse<>(); + ExceededCompRuleEvent eventData = null; + try{ + eventData = JSONObject.parseObject(payload, ExceededCompRuleEvent.class); + }catch(Exception err){ + result.setSuccess(false); + result.setMessage("Convert Payload failed:["+err.getMessage()+"]"); + } + if(eventData!=null){ + result = compRuleSendNotificationEvevnt(eventData); + } + return result; + } + + public BaseApiResponse compRuleSendNotificationEvevnt(ExceededCompRuleEvent event){ + BaseApiResponse apiResponse = new BaseApiResponse<>(); + + String compRuleId = event.getComplimentaryRuleId(); + boolean isExceededSoftLimit = false; + boolean isExceededTotalLimit = false; + + //TO*DO Misc Charge notification by Corso 20260915 完成 / 20260916 + //拿到CompRuleId 获取到对应的Booking + CfgCompRule byCompRuleId = cfgCompRuleDao.findByCompRuleId(compRuleId); + List bookings = bookingDao.findByDlReservationId(byCompRuleId.getDlReservationId()); + Bookings originBooking = new Bookings(); + + //判断是否为主Booking (非WaitList 且 非ApprovalRouting) + for (Bookings booking : bookings) { + if (!Strings.CS.containsAny(booking.getApproveStatus(),"W","A") && booking.getDlReservationId() !=null){ + originBooking = booking; + break; + } + } + + //TODO 根据CompRuleId 查到对应的CompRule 是超过了SoftLimit 还是 Total Limit 需要CCJ去拿到CompRule For CCJ By Corso 2026-09-16 + //查看超过SoftLimit/TotalLimit后 修改对应参数 + BaseApiResponse> listBaseApiResponse = + daylightApiService.daylightGetCompRulesByAccount(originBooking.getDlPropertyId(), byCompRuleId.getAccountId()); + + CompRuleResponse crr = null; + + for (CompRuleResponse c : listBaseApiResponse.getData()) { + if (c.getId().equals(compRuleId)) { + crr = c; + break; + } + } + + if (crr != null) { + String miscChargeValue = originBooking.getMiscCharge(); + + if (miscChargeValue != null && !miscChargeValue.isBlank() + && crr.getComplimentaryRuleLimits() != null) { + + BigDecimal miscCharge = new BigDecimal(miscChargeValue); + + CompRuleResponse.ComplimentaryRuleLimits limits = + crr.getComplimentaryRuleLimits(); + + // Soft Total Limit + if (limits.getSoftTotalLimit() != null + && limits.getSoftTotalLimit().getAmountLimit() != null + && limits.getSoftTotalLimit().getAmountLimit().getAmount() != null) { + + BigDecimal softLimit = + limits.getSoftTotalLimit() + .getAmountLimit() + .getAmount(); + + if (softLimit.compareTo(miscCharge) > 0) { + isExceededSoftLimit = true; + } + } + + // Total Limit + if (limits.getTotalLimit() != null + && limits.getTotalLimit().getAmountLimit() != null + && limits.getTotalLimit().getAmountLimit().getAmount() != null) { + + BigDecimal totalLimit = + limits.getTotalLimit() + .getAmountLimit() + .getAmount(); + + if (totalLimit.compareTo(miscCharge) > 0) { + isExceededTotalLimit = true; + } + } + } + } + + //去DB中拿到当前CompRule对应的记录 检查是否已经发送过对应的Notification + if (byCompRuleId != null) { + boolean isSandSoftLimit = byCompRuleId.getIsSendSoftLimit() == 1; + boolean isSandTotalLimit = byCompRuleId.getIsSendTotalLimit() == 1; + if ((isSandSoftLimit && isExceededSoftLimit) || (isSandTotalLimit && isExceededTotalLimit)) { + //如果发送过 直接跳过该条记录 + return apiResponse; + } + } + //如果没有发送过 调用apns接口去发送 Notification + if (isExceededSoftLimit){ + //调用APNs方法 发送超过Soft Limit Notification + LogUtil.info(BookingService.class,"Send Exceeded Soft Limit Notification Booking Dl ReservationId = "+originBooking.getDlReservationId()+ + ", PlayerId = "+originBooking.getPlayerId()); + +// //设置Notification 内容 + String notificationTitle = "#EN#1 Misc-Charge Reminder#EN#" + + "#CN#1 個雜費提示#CN#"; + String notificationContent = "#EN#[Misc-charge reminder] Please be informed that the Misc charge of #{playerId} ID is reach to 50%.#EN#" + + "#CN#[客人雜費提醒 ] 請注意! 閣下提供給#{playerId} 的雜費已經到達50%。#CN#"; + String[] placeholders = { + "{playerId}" + }; + LogUtil.info(BookingService.class,"Set notification info"); + String[] values = { + StringUtils.defaultString(originBooking.getPlayerId()), + }; + LogUtil.info(BookingService.class,"Replace each"); + notificationContent = StringUtils.replaceEach(notificationContent, placeholders, values); + String transactionId = "BKCRSL"+originBooking.getDlReservationId(); + LogUtil.info(BookingService.class,"Get device token from DB Start"); + SysLogDeviceToken deviceToken = sysLogDeviceTokenRepository.findByUserId(originBooking.getCreatedBy().trim()); + LogUtil.info(BookingService.class,"Get device token from DB:" + (deviceToken == null ? "null" : deviceToken.getDeviceToken())); + String deviceTokenValue =null; + String finalTitle = null; + if(deviceToken!=null){ + deviceTokenValue = deviceToken.getDeviceToken(); + finalTitle = notificationUtil.distinguishLanguage(notificationTitle,deviceToken.getUseLanguageType()); + notificationContent = notificationUtil.distinguishLanguage(notificationContent,deviceToken.getUseLanguageType()); + + } +// NOTIFICATION + apnNotificationService.pushApnMessage(transactionId,deviceTokenValue,finalTitle,notificationContent,true); + } else if (isExceededTotalLimit) { + //调用APNs方法 发送超过Total Limit Notificaiton + LogUtil.info(BookingService.class,"Send Exceeded Total Limit Notification Booking Dl ReservationId = "+originBooking.getDlReservationId()+ + ", PlayerId = "+originBooking.getPlayerId()); + +// //设置Notification 内容 + String notificationTitle = "#EN#1 Misc-Charge Reminder#EN#" + + "#CN#1 個雜費提示#CN#"; + String notificationContent = "#EN#[Mis-charge reminder] Please be informed that the misc charge of #{playerId} ID is reach to 100%, call FO to settle it ASAP. If you want to increase the limit amount, please contact related departments. \n#EN#" + + "#CN#[客人雜費提醒 ] 請注意! 閣下提供給#{playerId} 的雜費已經到達100%. 請儘快聯絡相關部門。#CN#"; + String[] placeholders = { + "{playerId}" + }; + LogUtil.info(BookingService.class,"Set notification info"); + String[] values = { + StringUtils.defaultString(originBooking.getPlayerId()), + }; + LogUtil.info(BookingService.class,"Replace each"); + notificationContent = StringUtils.replaceEach(notificationContent, placeholders, values); + String transactionId = "BKCRTL"+originBooking.getDlReservationId(); + LogUtil.info(BookingService.class,"Get device token from DB Start"); + SysLogDeviceToken deviceToken = sysLogDeviceTokenRepository.findByUserId(originBooking.getCreatedBy().trim()); + LogUtil.info(BookingService.class,"Get device token from DB:" + (deviceToken == null ? "null" : deviceToken.getDeviceToken())); + String deviceTokenValue =null; + String finalTitle = null; + if(deviceToken!=null){ + deviceTokenValue = deviceToken.getDeviceToken(); + finalTitle = notificationUtil.distinguishLanguage(notificationTitle,deviceToken.getUseLanguageType()); + notificationContent = notificationUtil.distinguishLanguage(notificationContent,deviceToken.getUseLanguageType()); + + } +// NOTIFICATION + apnNotificationService.pushApnMessage(transactionId,deviceTokenValue,finalTitle,notificationContent,true); + } + + return apiResponse; + + } +} diff --git a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationNotificationHandler.java b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationNotificationHandler.java index 0ec0d32..2e1957b 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationNotificationHandler.java +++ b/src/main/java/com/mgm/mcrm/apigateway/daylight/webhook/eventhandlers/impl/ReservationNotificationHandler.java @@ -1,10 +1,12 @@ package com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl; +import com.alibaba.fastjson2.JSONArray; 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.services.DaylightApiService; import com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation.ReservationNotificationEvent; import com.mgm.mcrm.apigateway.daylight.webhook.dtos.reservation.StatusCodeEntity; import com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.DaylightWebhookHandler; @@ -14,6 +16,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @@ -26,10 +29,11 @@ public class ReservationNotificationHandler implements DaylightWebhookHandler { private final BookingService bookingService; + private final DaylightApiService daylightApiService; @Override - public BaseApiResponse handle(String payload, Map headers) throws Exception { - BaseApiResponse result = new BaseApiResponse(); + public BaseApiResponse handle(String payload, Map headers) throws Exception { + BaseApiResponse result = new BaseApiResponse<>(); ReservationNotificationEvent eventData = null; try{ eventData = JSONObject.parseObject(payload, ReservationNotificationEvent.class); @@ -101,6 +105,36 @@ public class ReservationNotificationHandler implements DaylightWebhookHandler { String etaTime = dateTime.format(DateTimeFormatter.ofPattern("HH:mm")); result.setEta(etaTime); } + BaseApiResponse response = + daylightApiService.getDaylightReservationByDlId(eventData.getReservationId(), eventData.getPropertyId()); + + if (response.isSuccess()) { + JSONObject embedded = response.getData().getJSONObject("_embedded"); + if (embedded != null) { + JSONArray customFields = embedded.getJSONArray("customFields"); + + if (customFields != null) { + for (int j = 0; j < customFields.size(); j++) { + JSONObject jsonObject = customFields.getJSONObject(j); + String value = jsonObject.getString("value"); + JSONObject valueJson = JSONObject.parseObject(value); + String booker = valueJson.getString("Booker"); + + if (StringUtils.isNotBlank(booker)) { + result.setCreatedBy(booker); + result.setBooker(booker); + } + } + } + } + + JSONArray jsonArray = response.getData().getJSONArray("externalIds"); + if (!CollectionUtils.isEmpty(jsonArray)) { + String dlExternalNo = jsonArray.getJSONObject(0).getString("number"); + result.setDlExternalNo(dlExternalNo); + } + } + return result; } 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 index ef19c38..21fbdf1 100644 --- 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 @@ -1,6 +1,6 @@ package com.mgm.mcrm.apigateway.daylight.webhook.eventhandlers.impl; -import com.alibaba.fastjson.JSONObject; +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; @@ -21,8 +21,8 @@ public class RoomTypeDateAvailabilityChangedHandler implements DaylightWebhookHa private final RoomCriteriaDaylightService roomCriteriaDaylightService; @Override - public BaseApiResponse handle(String payload, Map headers) { - BaseApiResponse result = new BaseApiResponse(); + public BaseApiResponse handle(String payload, Map headers) { + BaseApiResponse result = new BaseApiResponse<>(); RoomTypeDateAvailabilityChangedEvent eventData = null; try{ eventData = JSONObject.parseObject(payload, RoomTypeDateAvailabilityChangedEvent.class); 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 index f2f040d..c1bacf5 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/notification/controllers/NotificationController.java +++ b/src/main/java/com/mgm/mcrm/apigateway/notification/controllers/NotificationController.java @@ -27,7 +27,7 @@ public class NotificationController { private EmailProxySetting emailProxySetting; @PostMapping(value = "/send_email", produces = "application/json;charset=UTF-8") - public ResponseEntity handleSendEmailRequest( + public ResponseEntity> handleSendEmailRequest( @RequestBody(required = true) SendEmailRequest requestData, HttpServletRequest request ){ @@ -35,7 +35,7 @@ public class NotificationController { String appId = request.getHeader("application-id"); String securityKey = request.getHeader("authorization"); - BaseApiResponse result = emailNotificationService.sendEmailByRequest(requestData,emailProxySetting); + BaseApiResponse result = emailNotificationService.sendEmailByRequest(requestData,emailProxySetting); return ResponseEntity.status(HttpStatus.OK).body(result); } @@ -44,7 +44,7 @@ public class NotificationController { * showTemp 为 HTML 正文,优先于 content;有 cc 时按群发模式发送。 */ @PostMapping(value = "/proxy/send_email", produces = "application/json;charset=UTF-8") - public ResponseEntity handleSendEmailProxyRequest( + public ResponseEntity> handleSendEmailProxyRequest( @RequestBody(required = true) EmailProxyRequest requestData, HttpServletRequest request ) { @@ -64,7 +64,7 @@ public class NotificationController { // 有 cc 时用群发模式(false),让 to + cc 在同一封邮件里 sendEmailRequest.setSendSeparately(StringUtils.isBlank(requestData.getCc())); - BaseApiResponse result = emailNotificationService.sendEmailByRequest(sendEmailRequest, emailProxySetting); + 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/services/ApnNotificationService.java b/src/main/java/com/mgm/mcrm/apigateway/notification/services/ApnNotificationService.java index 861e335..c9cfd38 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/notification/services/ApnNotificationService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/notification/services/ApnNotificationService.java @@ -33,12 +33,12 @@ public class ApnNotificationService { private final Logger logger = LoggerFactory.getLogger(ApnNotificationService.class); - public BaseApiResponse pushApnMessage(String transactionId, String deviceToken, String title, String body, boolean isWaitResponse) { + public BaseApiResponse pushApnMessage(String transactionId, String deviceToken, String title, String body, boolean isWaitResponse) { return pushApnMessage(transactionId, deviceToken, title, body, isWaitResponse, null); } - public BaseApiResponse pushApnMessage(String transactionId, String deviceToken, String title, String body, boolean isWaitResponse, String confirmNo) { - BaseApiResponse result = new BaseApiResponse(); + public BaseApiResponse pushApnMessage(String transactionId, String deviceToken, String title, String body, boolean isWaitResponse, String confirmNo) { + BaseApiResponse result = new BaseApiResponse<>(); if(StringUtils.isBlank(deviceToken)){ result.setSuccess(false); result.setMessage("Device Token is empty"); 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 index 51cbfe9..08b554f 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/notification/services/EmailNotificationService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/notification/services/EmailNotificationService.java @@ -21,8 +21,8 @@ import java.util.Properties; public class EmailNotificationService { private final Logger logger = LoggerFactory.getLogger(EmailNotificationService.class); - public BaseApiResponse sendEmailByRequest(SendEmailRequest requestData, EmailProxySetting emailConfig) { - BaseApiResponse result = new BaseApiResponse(); + public BaseApiResponse sendEmailByRequest(SendEmailRequest requestData, EmailProxySetting emailConfig) { + BaseApiResponse result = new BaseApiResponse<>(); try { JavaMailSenderImpl emailSender = new JavaMailSenderImpl(); emailSender.setHost(emailConfig.getSmtpServer()); diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/NotificationProxyController.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/NotificationProxyController.java index 50bf4d0..1a332f4 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/NotificationProxyController.java +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/NotificationProxyController.java @@ -40,7 +40,7 @@ public class NotificationProxyController { private NotificationProxyService notificationProxyService; @RequestMapping("/api/notification/proxy/**") - public ResponseEntity invokeNotificationApiProxy(HttpServletRequest request, @RequestBody SendNotificationRequest notificationRequest) throws Exception { + public ResponseEntity> invokeNotificationApiProxy(HttpServletRequest request, @RequestBody SendNotificationRequest notificationRequest) throws Exception { LoginUserInfo requestUser = SysCommon.parseUserFromToken(request.getHeader("Authorization")); return ResponseEntity.status(HttpStatus.OK).body(notificationProxyService.sendNotificationByRequest(notificationRequest,requestUser.getLoginId())); } 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 index fabfb8e..5d7a74e 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/WebProxyController.java +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/controllers/WebProxyController.java @@ -1,7 +1,6 @@ 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; @@ -64,6 +63,7 @@ public class WebProxyController { throw new Exception("Invoke daylight token failed"); } headers.set("authorization","Bearer "+daylightApiToken.getAccessToken()); + return webProxyService.invokeRequest( WebProxyService.DAYLIGHT_API, HttpMethod.valueOf(request.getMethod()), diff --git a/src/main/java/com/mgm/mcrm/apigateway/proxy/services/NotificationProxyService.java b/src/main/java/com/mgm/mcrm/apigateway/proxy/services/NotificationProxyService.java index b9c0aaf..1dc63e1 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/proxy/services/NotificationProxyService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/proxy/services/NotificationProxyService.java @@ -29,8 +29,8 @@ public class NotificationProxyService { @Resource private ApnNotificationService apnNotificationService; - public BaseApiResponse sendNotificationByRequest(SendNotificationRequest requestData, String senderId) { - BaseApiResponse result = new BaseApiResponse(); + public BaseApiResponse sendNotificationByRequest(SendNotificationRequest requestData, String senderId) { + BaseApiResponse result = new BaseApiResponse<>(); String[] userIds = requestData.getEmployeeId().split("[;,]"); String transactionId = requestData.getTransactionId(); if(StringUtils.isBlank(transactionId)){ @@ -62,7 +62,7 @@ public class NotificationProxyService { } sysLogNotificationRepository.save(notificationLog); //-- Process APN sending - BaseApiResponse sendResult = apnNotificationService.pushApnMessage( + BaseApiResponse sendResult = apnNotificationService.pushApnMessage( transactionId, deviceTokenValue, notificationTitle, 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 index 65fa371..62b7b9f 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/reservation/models/BookingsView.java +++ b/src/main/java/com/mgm/mcrm/apigateway/reservation/models/BookingsView.java @@ -1,7 +1,7 @@ package com.mgm.mcrm.apigateway.reservation.models; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; import com.fasterxml.jackson.annotation.JsonFormat; import jakarta.persistence.*; import lombok.Data; 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 index 1f3e486..74764e8 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/reservation/repositories/BookingDao.java +++ b/src/main/java/com/mgm/mcrm/apigateway/reservation/repositories/BookingDao.java @@ -2,7 +2,6 @@ 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; @@ -13,4 +12,6 @@ public interface BookingDao extends JpaRepository { List findByDlReservationId(String dlReservationId); Bookings findFirstByDlProfileIdAndPlayerIdIsNotNull(String dlProfileId); + + List findByDlExternalNo(String dlExternalNo); } diff --git a/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java b/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java index ee72804..1b7dc48 100644 --- a/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java +++ b/src/main/java/com/mgm/mcrm/apigateway/reservation/services/BookingService.java @@ -33,17 +33,13 @@ 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 lombok.extern.java.Log; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Strings; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.concurrent.TimeUnit; @@ -80,131 +76,134 @@ public class BookingService { private ApnNotificationService apnNotificationService; public void processDaylightUpdateBooking(Bookings daylightBooking) throws Exception { - if(daylightBooking!=null){ - String dlReservationId=daylightBooking.getDlReservationId(); - String confirmNo = daylightBooking.getConfirmationNo(); - String dbBookingStatus = null; - LocalDateTime checkInDate = null; - LocalDateTime checkOutDate = null; - if(StringUtils.isNotBlank(confirmNo) - && StringUtils.isNotBlank(dlReservationId) - ){ - List dbBookings = bookingDao.findByDlReservationId(dlReservationId); - if (CollectionUtils.isEmpty(dbBookings)) { - dbBookings = bookingDao.findByConfirmationNo(confirmNo); + if (daylightBooking == null) + return; + + String dlReservationId = daylightBooking.getDlReservationId(); + String confirmNo = daylightBooking.getConfirmationNo(); + String dlExternalNo = daylightBooking.getDlExternalNo(); + + List dbBookings = null; + if (StringUtils.isNotEmpty(dlExternalNo)) { + dbBookings = bookingDao.findByDlExternalNo(dlExternalNo); + } + + if (CollectionUtils.isEmpty(dbBookings) && StringUtils.isNotEmpty(dlReservationId)) { + TimeUnit.SECONDS.sleep(2); + dbBookings = bookingDao.findByDlReservationId(dlReservationId); + } + + if (CollectionUtils.isEmpty(dbBookings) && StringUtils.isNotEmpty(confirmNo)) { + TimeUnit.SECONDS.sleep(2); + dbBookings = bookingDao.findByConfirmationNo(confirmNo); + } + + Bookings dbBooking = null; + LocalDateTime checkInDate = null; + LocalDateTime checkOutDate = null; + String dbBookingStatus = null; + if(!CollectionUtils.isEmpty(dbBookings)) { + for(Bookings booking:dbBookings){ + if("N".equalsIgnoreCase(booking.getApproveStatus())) { + dbBooking = booking; + dbBookingStatus = dbBooking.getLastOperaStatus(); + checkInDate = dbBooking.getCheckinDate(); + checkOutDate = dbBooking.getCheckoutDate(); + break; } - - 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; - dbBookingStatus = dbBooking.getLastOperaStatus(); - checkInDate = dbBooking.getCheckinDate(); - checkOutDate = dbBooking.getCheckoutDate(); - break; - } - } - } - if(dbBooking!=null){ - this.mergeDaylightToDbBooking(daylightBooking,dbBooking); - }else{ - dbBooking = this.createBookingFromDaylight(daylightBooking); - } - bookingDao.save(dbBooking); - - CfgProperties property = propertyDao.findByDlPropertyId(dbBooking.getDlPropertyId()); - - //Send Notification to Booker when booking lastOpera status Changed - - LogUtil.info(BookingService.class,"Daylight webhook updated, prepare send notification"); - LogUtil.info(BookingService.class,"DaylightBooking status=" + daylightBooking.getLastOperaStatus()); - LogUtil.info(BookingService.class,"Db booking status=" + dbBookingStatus); - if (Strings.CS.equals(daylightBooking.getLastOperaStatus(),"RESERVED") && !Strings.CS.equals(dbBookingStatus,daylightBooking.getLastOperaStatus())){ - LogUtil.info(BookingService.class,"Booking last opera status changed, DB Record status:"+dbBookingStatus+", DlBooking status:"+daylightBooking.getLastOperaStatus()); - //设置Notification 内容 - String notificationTitle = "#EN# Room status updated#EN#" + - "#CN#房間狀態已更改#CN#"; - String notificationContent = "#EN#Player {playerId}, {propertyName}, {roomCode},room status changed to reserved.#EN#" + - "#CN#Player {playerId}, {propertyName}, {roomCode}, 房間狀態已預訂。#CN#"; - String[] placeholders = { - "{playerId}", "{propertyName}", "{roomCode}" - }; - LogUtil.info(BookingService.class,"Set notification info"); - String[] values = { - StringUtils.defaultString(dbBooking.getPlayerId()), - StringUtils.defaultString(property == null ? null : property.getName()), - StringUtils.defaultString(dbBooking.getRoomTypeCode()) - }; - LogUtil.info(BookingService.class,"Replace each"); - notificationContent = StringUtils.replaceEach(notificationContent, placeholders, values); - String transactionId = "DLBKSC"+dbBooking.getId(); - LogUtil.info(BookingService.class,"Get device token from DB Start"); - SysLogDeviceToken deviceToken = sysLogDeviceTokenRepository.findByUserId(StringUtils.trim(dbBooking.getCreatedBy())); - LogUtil.info(BookingService.class,"Get device token from DB:" + (deviceToken == null ? "null" : deviceToken.getDeviceToken())); - String deviceTokenValue =null; - String finalTitle = null; - String finalContent = null; - if(deviceToken!=null){ - deviceTokenValue = deviceToken.getDeviceToken(); - finalTitle = notificationUtil.distinguishLanguage(notificationTitle,deviceToken.getUseLanguageType()); - notificationContent = notificationUtil.distinguishLanguage(notificationContent,deviceToken.getUseLanguageType()); - - } -// NOTIFICATION - apnNotificationService.pushApnMessage(transactionId,deviceTokenValue,finalTitle,notificationContent,true); - } - - //Send notification to Booker when Booking C/I | C/O date changed - if ((checkInDate != null && !checkInDate.equals(daylightBooking.getCheckinDate()))|| - (checkOutDate != null && !checkOutDate.equals(daylightBooking.getCheckoutDate()))) { - LogUtil.info(BookingService.class,"Booking date changed, DB Record C/I:"+checkInDate+", DB Record C/O:"+checkOutDate+ - ", Dl Record C/I"+daylightBooking.getCheckinDate()+", Dl Record C/O"+daylightBooking.getCheckoutDate()); - - - //设置Notification 内容 - String notificationTitle = "#EN# Room status updated#EN#" + - "#CN#房間狀態已更改#CN#"; - String notificationContent = "#EN#Player {playerId}, {propertyName}, {roomCode},Stay date changed.#EN#" + - "#CN#Player {playerId}, {propertyName}, {roomCode}, 住宿日期已變更。#CN#"; - String[] placeholders = { - "{playerId}", "{propertyName}", "{roomCode}" - }; - String[] values = { - StringUtils.defaultString(dbBooking.getPlayerId()), - StringUtils.defaultString(property == null ? null : property.getName()), - StringUtils.defaultString(dbBooking.getRoomTypeCode()) - }; - - notificationContent = StringUtils.replaceEach(notificationContent, placeholders, values); - String transactionId = "DLBKDC" + dbBooking.getId(); - - SysLogDeviceToken deviceToken = sysLogDeviceTokenRepository.findByUserId(StringUtils.trim(dbBooking.getCreatedBy())); - LogUtil.info(BookingService.class,"Get device token from DB:" + (deviceToken == null ? "null" : deviceToken.getDeviceToken())); - String deviceTokenValue = null; - String finalTitle = null; - String finalContent = null; - if (deviceToken != null) { - LogUtil.info(BookingService.class,"Get device Token success"); - deviceTokenValue = deviceToken.getDeviceToken(); - finalTitle = notificationUtil.distinguishLanguage(notificationTitle, deviceToken.getUseLanguageType()); - notificationContent = notificationUtil.distinguishLanguage(notificationContent, deviceToken.getUseLanguageType()); - } -// NOTIFICATION - apnNotificationService.pushApnMessage(transactionId, deviceTokenValue, finalTitle, notificationContent, true); - } - LogUtil.info(BookingService.class,"End send notification function"); } } + if(dbBooking!=null){ + LogUtil.info(getClass(), "Find db Booking, id=" + dbBooking.getId()); + this.mergeDaylightToDbBooking(daylightBooking,dbBooking); + }else{ + LogUtil.info(getClass(), "Not found db Booking"); + dbBooking = this.createBookingFromDaylight(daylightBooking); + } + bookingDao.save(dbBooking); + + CfgProperties property = propertyDao.findByDlPropertyId(dbBooking.getDlPropertyId()); + + //Send Notification to Booker when booking lastOpera status Changed + + LogUtil.info(BookingService.class,"Daylight webhook updated, prepare send notification"); + LogUtil.info(BookingService.class,"DaylightBooking status=" + daylightBooking.getLastOperaStatus()); + LogUtil.info(BookingService.class,"Db booking status=" + dbBookingStatus); + if (Strings.CS.equals(daylightBooking.getLastOperaStatus(),"RESERVED") && !Strings.CS.equals(dbBookingStatus,daylightBooking.getLastOperaStatus())){ + LogUtil.info(BookingService.class,"Booking last opera status changed, DB Record status:"+dbBookingStatus+", DlBooking status:"+daylightBooking.getLastOperaStatus()); + //设置Notification 内容 + String notificationTitle = "#EN# Room status updated#EN#" + + "#CN#房間狀態已更改#CN#"; + String notificationContent = "#EN#Player {playerId}, {propertyName}, {roomCode},room status changed to reserved.#EN#" + + "#CN#Player {playerId}, {propertyName}, {roomCode}, 房間狀態已預訂。#CN#"; + String[] placeholders = { + "{playerId}", "{propertyName}", "{roomCode}" + }; + LogUtil.info(BookingService.class,"Set notification info"); + String[] values = { + StringUtils.defaultString(dbBooking.getPlayerId()), + StringUtils.defaultString(property == null ? null : property.getName()), + StringUtils.defaultString(dbBooking.getRoomTypeCode()) + }; + LogUtil.info(BookingService.class,"Replace each"); + notificationContent = StringUtils.replaceEach(notificationContent, placeholders, values); + String transactionId = "DLBKSC"+dbBooking.getId(); + LogUtil.info(BookingService.class,"Get device token from DB Start"); + LogUtil.info(BookingService.class,"dbBooking.getCreatedBy()=" + dbBooking.getCreatedBy()); + SysLogDeviceToken deviceToken = sysLogDeviceTokenRepository.findByUserId(StringUtils.trim(dbBooking.getCreatedBy())); + LogUtil.info(BookingService.class,"Get device token from DB:" + (deviceToken == null ? "null" : deviceToken.getDeviceToken())); + String deviceTokenValue =null; + String finalTitle = null; + String finalContent = null; + if(deviceToken!=null){ + deviceTokenValue = deviceToken.getDeviceToken(); + finalTitle = notificationUtil.distinguishLanguage(notificationTitle,deviceToken.getUseLanguageType()); + notificationContent = notificationUtil.distinguishLanguage(notificationContent,deviceToken.getUseLanguageType()); + + } +// NOTIFICATION + apnNotificationService.pushApnMessage(transactionId,deviceTokenValue,finalTitle,notificationContent,true); + } + + //Send notification to Booker when Booking C/I | C/O date changed + if ((checkInDate != null && !checkInDate.equals(daylightBooking.getCheckinDate()))|| + (checkOutDate != null && !checkOutDate.equals(daylightBooking.getCheckoutDate()))) { + LogUtil.info(BookingService.class,"Booking date changed, DB Record C/I:"+checkInDate+", DB Record C/O:"+checkOutDate+ + ", Dl Record C/I"+daylightBooking.getCheckinDate()+", Dl Record C/O"+daylightBooking.getCheckoutDate()); + + + //设置Notification 内容 + String notificationTitle = "#EN# Room status updated#EN#" + + "#CN#房間狀態已更改#CN#"; + String notificationContent = "#EN#Player {playerId}, {propertyName}, {roomCode},Stay date changed.#EN#" + + "#CN#Player {playerId}, {propertyName}, {roomCode}, 住宿日期已變更。#CN#"; + String[] placeholders = { + "{playerId}", "{propertyName}", "{roomCode}" + }; + String[] values = { + StringUtils.defaultString(dbBooking.getPlayerId()), + StringUtils.defaultString(property == null ? null : property.getName()), + StringUtils.defaultString(dbBooking.getRoomTypeCode()) + }; + + notificationContent = StringUtils.replaceEach(notificationContent, placeholders, values); + String transactionId = "DLBKDC" + dbBooking.getId(); + + SysLogDeviceToken deviceToken = sysLogDeviceTokenRepository.findByUserId(StringUtils.trim(dbBooking.getCreatedBy())); + LogUtil.info(BookingService.class,"Get device token from DB:" + (deviceToken == null ? "null" : deviceToken.getDeviceToken())); + String deviceTokenValue = null; + String finalTitle = null; + String finalContent = null; + if (deviceToken != null) { + LogUtil.info(BookingService.class,"Get device Token success"); + deviceTokenValue = deviceToken.getDeviceToken(); + finalTitle = notificationUtil.distinguishLanguage(notificationTitle, deviceToken.getUseLanguageType()); + notificationContent = notificationUtil.distinguishLanguage(notificationContent, deviceToken.getUseLanguageType()); + } +// NOTIFICATION + apnNotificationService.pushApnMessage(transactionId, deviceTokenValue, finalTitle, notificationContent, true); + } + LogUtil.info(BookingService.class,"End send notification function"); } public Bookings findOneByDlProfileId(String dlProfileId) { @@ -212,46 +211,46 @@ public class BookingService { } // 第一条是daylight返回过来的booking,第二条是本地数据库的booking - public void mergeDaylightToDbBooking(Bookings daylightBooking, Bookings updateBooking) throws Exception { + public void mergeDaylightToDbBooking(Bookings daylightBooking, Bookings dbBooking) 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()); + if(!"PN".equals(dbBooking.getApproveStatus())) { + dbBooking.setCheckinDate(daylightBooking.getCheckinDate()); + dbBooking.setCheckoutDate(daylightBooking.getCheckoutDate()); + dbBooking.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()); + dbBooking.setConfirmationNo(daylightBooking.getConfirmationNo()); + dbBooking.setHotelCode(daylightBooking.getHotelCode()); + dbBooking.setDlProfileId(daylightBooking.getDlProfileId()); + dbBooking.setDlSourceId(daylightBooking.getDlSourceId()); + dbBooking.setDlGuaranteeTypeId(daylightBooking.getDlGuaranteeTypeId()); + dbBooking.setDlChannelId(daylightBooking.getDlChannelId()); + dbBooking.setDlAccountId(daylightBooking.getDlAccountId()); + dbBooking.setDlBlockId(daylightBooking.getDlBlockId()); + dbBooking.setDlExternalNo(daylightBooking.getDlExternalNo()); - if (updateBooking.getWaitlistId() != null) { - bookingDao.findById(updateBooking.getWaitlistId()).ifPresent(booking -> { + if (dbBooking.getWaitlistId() != null) { + bookingDao.findById(dbBooking.getWaitlistId()).ifPresent(booking -> { booking.setConfirmationNo(daylightBooking.getConfirmationNo()); bookingDao.save(booking); }); } -// updateBooking.setGroupId(daylightBooking.getGroupId()); - updateBooking.setDlReservationId(daylightBooking.getDlReservationId()); +// dbBooking.setGroupId(daylightBooking.getGroupId()); + dbBooking.setDlReservationId(daylightBooking.getDlReservationId()); // 通过 dlRoomTypeId 查找 room type(与 createBookingFromDaylight 流程保持一致) CfgRoomTypes daylightRoomType = roomTypeDao.findByDlRoomTypeId(daylightBooking.getDlRoomTypeId()); if(daylightRoomType!=null) { - updateBooking.setRoomTypeId(daylightRoomType.getId()); - updateBooking.setRoomTypeCode(daylightRoomType.getCode()); + dbBooking.setRoomTypeId(daylightRoomType.getId()); + dbBooking.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())); + if(room.getPropertyId().equals(dbBooking.getPropertyId())) { + //dbBooking.setRoom(room.getName()); + dbBooking.setRoomId(Math.toIntExact(room.getId())); break; } } @@ -267,52 +266,52 @@ public class BookingService { List bookingTypeOperas = daylightViewDao.findByMarketCodeIdAndRateCodeId(operaMarketCodeId,operaRateCodeId); if(!bookingTypeOperas.isEmpty()) { Long operaBookingTypeId = bookingTypeOperas.getFirst().getBookingTypeId(); - updateBooking.setBookingTypeId(Math.toIntExact(operaBookingTypeId)); + dbBooking.setBookingTypeId(Math.toIntExact(operaBookingTypeId)); } } if("CANCELED".equals(daylightBooking.getLastOperaStatus())) { - updateBooking.setApproveStatus("C"); + dbBooking.setApproveStatus("C"); daylightBooking.setApproveStatus("C"); }else { - if("C".equals(updateBooking.getApproveStatus())||daylightBooking.getConfirmationNo()!=null) { - updateBooking.setApproveStatus("N"); + if("C".equals(dbBooking.getApproveStatus())||daylightBooking.getConfirmationNo()!=null) { + dbBooking.setApproveStatus("N"); daylightBooking.setApproveStatus("N"); } } - updateBooking.setLastOperaStatus(daylightBooking.getLastOperaStatus()); + dbBooking.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(StringUtils.isEmpty(dbBooking.getMember())) { + if(StringUtils.isNotEmpty(dbBooking.getPlayerId())) { + ServiceResult serviceResult = playerService.findWebPlayerProfileByPlayerId(dbBooking.getPlayerId()); if(serviceResult.isSuccess()) { PlayerProfileApiResult profile = (PlayerProfileApiResult) serviceResult.getReturnData(); - updateBooking.setMemberLastName(profile.getName()); - updateBooking.setMemberTitle(profile.getSalutation()); - updateBooking.setMember(JSONObject.toJSONString(profile)); + dbBooking.setMemberLastName(profile.getName()); + dbBooking.setMemberTitle(profile.getSalutation()); + dbBooking.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()); + String booker = StringUtils.trim(dbBooking.getBooker()); + if(StringUtils.isEmpty(booker)) getUser=true; + if(daylightBooking.getCreatedBy()!=null && !daylightBooking.getCreatedBy().equals(dbBooking.getCreatedBy())) { +// dbBooking.setCreatedBy(daylightBooking.getCreatedBy()); getUser=true; } if(getUser) { - if(!StringUtils.isEmpty(updateBooking.getCreatedBy())) { - ServiceResult userInfoRes = mgmUserService.findUserInfoByUserId(updateBooking.getCreatedBy()); + if(StringUtils.isNotEmpty(dbBooking.getCreatedBy())) { + ServiceResult userInfoRes = mgmUserService.findUserInfoByUserId(dbBooking.getCreatedBy()); if(userInfoRes.isSuccess()) { UserInfoApiRecord userInfo = (UserInfoApiRecord)userInfoRes.getReturnData(); - updateBooking.setBooker(userInfo.getUserName()); - updateBooking.setCreatedByName(userInfo.getUserName()); + dbBooking.setBooker(userInfo.getUserName()); + dbBooking.setCreatedByName(userInfo.getUserName()); } } } - updateBooking.setUpdatedAt(LocalDateTime.now()); + dbBooking.setUpdatedAt(LocalDateTime.now()); } public Bookings createBookingFromDaylight(Bookings daylightBooking) throws Exception { @@ -412,7 +411,7 @@ public class BookingService { dbBooking.setDlReservationId(daylightBooking.getDlReservationId()); //get player info - if(!StringUtils.isEmpty(dbBooking.getPlayerId())) { + if(StringUtils.isNotEmpty(dbBooking.getPlayerId())) { ServiceResult serviceResult = playerService.findWebPlayerProfileByPlayerId(dbBooking.getPlayerId()); if(serviceResult.isSuccess()) { PlayerProfileApiResult profile = (PlayerProfileApiResult) serviceResult.getReturnData(); @@ -423,7 +422,8 @@ public class BookingService { } //get user info // 这个判断不会走进去,因为createdBy必定是null,因为eventData没有这个字段 by Mauve 20260806 - if(!StringUtils.isEmpty(dbBooking.getCreatedBy())) { + // 现在可以拿到createdBy了 by Mauve 20260920 + if(StringUtils.isNotEmpty(dbBooking.getCreatedBy())) { ServiceResult userInfoRes = mgmUserService.findUserInfoByUserId(dbBooking.getCreatedBy()); if(userInfoRes.isSuccess()) { UserInfoApiRecord userInfo = (UserInfoApiRecord)userInfoRes.getReturnData(); diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 3b3454e..68ede25 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -127,7 +127,6 @@ server: logging: config: classpath:logback.xml - springdoc: swagger-ui: path: /swagger-ui.html @@ -153,4 +152,7 @@ apns: # Dev host host: api.sandbox.push.apple.com # Production Host -# host: api.push.apple.com \ No newline at end of file +# host: api.push.apple.com + +daylight: + url: http://localhost:9087/api/daylight/proxy \ No newline at end of file