From 5274fb8d6445fe5ab6119037b361e55e69ebebac Mon Sep 17 00:00:00 2001 From: mocheng <3057647414@qq.com> Date: Mon, 28 Jul 2025 01:01:25 +0800 Subject: [PATCH] =?UTF-8?q?SOS=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/sis/config/SOSAppConfig.java | 42 +-- .../sis/service/impl/ApiServiceImpl.java | 268 ++++++++++++++++-- .../org/dromara/sis/task/DataSyncTask.java | 2 +- .../Sis/src/main/resources/application.yml | 5 +- .../src/main/resources/application.properties | 4 +- 5 files changed, 268 insertions(+), 53 deletions(-) diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/config/SOSAppConfig.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/config/SOSAppConfig.java index 94788ba..a450d95 100644 --- a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/config/SOSAppConfig.java +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/config/SOSAppConfig.java @@ -1,21 +1,21 @@ -package org.dromara.sis.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; - -/** - * 应用配置类 - */ -@Configuration -public class SOSAppConfig { - - /** - * 配置 RestTemplate 用于 API 调用 - * @return RestTemplate 实例 - */ - @Bean - public RestTemplate restTemplate() { - return new RestTemplate(); - } -} +//package org.dromara.sis.config; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.web.client.RestTemplate; +// +///** +// * 应用配置类 +// */ +//@Configuration +//public class SOSAppConfig { +// +// /** +// * 配置 RestTemplate 用于 API 调用 +// * @return RestTemplate 实例 +// */ +// @Bean +// public RestTemplate restTemplate() { +// return new RestTemplate(); +// } +//} diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/ApiServiceImpl.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/ApiServiceImpl.java index e16dc0e..b49606a 100644 --- a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/ApiServiceImpl.java +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/service/impl/ApiServiceImpl.java @@ -4,16 +4,17 @@ import lombok.extern.slf4j.Slf4j; import org.dromara.sis.service.ApiService; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.*; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** - * API 服务实现类 - * 负责与外部 API 进行通信 + * API服务实现类,支持获取Token、刷新Token和401响应处理 */ @Service @Slf4j @@ -22,34 +23,43 @@ public class ApiServiceImpl implements ApiService { @Value("${api.url}") private String apiUrl; + @Value("${api.authUrl}") + private String authUrl; // 认证URL: /sos/v1/mntn/account/appId/token + + @Value("${api.refreshUrl}") + private String refreshUrl; // 刷新Token URL: /sos/v1/mntn/account/refresh/token + + @Value("${api.appId}") + private String appId; + + @Value("${api.appCode}") + private String appCode; + private final RestTemplate restTemplate; + // 缓存Token信息 + private static final Map tokenCache = new ConcurrentHashMap<>(); + public ApiServiceImpl(RestTemplate restTemplate) { this.restTemplate = restTemplate; } @Override + @Retryable(value = {HttpClientErrorException.Unauthorized.class}, maxAttempts = 2, backoff = @Backoff(delay = 1000)) public Map fetchAlarmRecords(int pageNum, int pageSize) { + String token = getAccessToken(); + + // 设置请求头,添加Token + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("token", token); // 根据接口要求,使用"Token"而非"Authorization" + + // 构建请求体 + Map requestBody = buildRequestParams(pageNum, pageSize); + + log.info("请求参数:{}", requestBody); try { - // 设置请求头 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - - // 构建请求体 - Map requestBody = new HashMap<>(); - requestBody.put("PageSize", pageSize); - requestBody.put("PageNum", pageNum); - - List>> filters = List.of( - List.of(Map.of("Key", "FinishTime", "Type", "str", "Value", "2025-07-27")) - ); - requestBody.put("Filters", filters); - - requestBody.put("OrderKey", "StartTime"); - requestBody.put("Desc", "0"); - requestBody.put("OrFirst", true); - - // 发送请求 + // 发送带Token的请求 HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); ResponseEntity response = restTemplate.exchange( apiUrl, @@ -57,23 +67,225 @@ public class ApiServiceImpl implements ApiService { requestEntity, Map.class ); + log.info("请求返回:{}", response); - // 处理响应 if (response.getStatusCode() == HttpStatus.OK) { Map result = response.getBody(); if (result != null && result.get("Status").equals(0)) { return result; } else { - log.error("API 返回错误状态: {}", result); + log.error("API返回错误状态: {}", result); return null; } } else { - log.error("API 请求失败,状态码: {}", response.getStatusCode()); + log.error("API请求失败,状态码: {}", response.getStatusCode()); return null; } + } catch (HttpClientErrorException.Unauthorized e) { + // 捕获401错误,清除缓存的Token并重试 + log.warn("Token已过期,清除缓存并尝试重新获取: {}", e.getMessage()); + tokenCache.remove(appId); + // 重新获取Token并再次调用(由@Retryable注解处理) + throw e; } catch (Exception e) { - log.error("调用 API 异常: {}", e.getMessage(), e); + log.error("调用API异常: {}", e.getMessage(), e); return null; } } + + /** + * 获取访问Token + */ + private synchronized String getAccessToken() { + // 检查缓存中是否有有效的Token + TokenInfo tokenInfo = tokenCache.get(appId); + if (tokenInfo != null && !isTokenExpired(tokenInfo)) { + return tokenInfo.getAccessToken(); + } + + // 尝试刷新Token + if (tokenInfo != null && tokenInfo.getRefreshToken() != null) { + log.info("尝试刷新现有Token..."); + try { + String refreshedToken = refreshAccessToken(tokenInfo.getRefreshToken()); + if (refreshedToken != null) { + return refreshedToken; + } + } catch (Exception e) { + log.warn("刷新Token失败,将重新获取: {}", e.getMessage()); + } + } + + // 缓存中没有有效Token或刷新失败,重新获取 + log.info("正在获取新的访问Token..."); + try { + // 构建获取Token的请求体 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + Map body = new HashMap<>(); + body.put("AppId", appId); + body.put("AppCode", appCode); + + HttpEntity> request = new HttpEntity<>(body, headers); + + // 发送获取Token的请求 + ResponseEntity response = restTemplate.postForEntity( + authUrl, + request, + Map.class + ); + log.info("接口响应:{},状态码:{}",response,response.getStatusCode()); + + if (response.getStatusCode() == HttpStatus.OK) { + Map tokenResponse = response.getBody(); + if (tokenResponse != null && tokenResponse.get("Status").equals(0)) { + String accessToken = (String) tokenResponse.get("Token"); + String refreshToken = (String) tokenResponse.get("RefreshToken"); + Integer expireIn = (Integer) tokenResponse.get("ExpireIn"); + + // 缓存新获取的Token,设置过期时间(提前1分钟,避免临界点问题) + TokenInfo newTokenInfo = new TokenInfo( + accessToken, + refreshToken, + System.currentTimeMillis() + (expireIn - 60) * 1000 + ); + tokenCache.put(appId, newTokenInfo); + + log.info("成功获取新的访问Token,有效期: {}秒", expireIn); + return accessToken; + } + } + + log.error("获取Token失败,状态码: {}", response.getStatusCode()); + throw new RuntimeException("获取访问Token失败"); + } catch (Exception e) { + log.error("获取Token异常: {}", e.getMessage(), e); + throw new RuntimeException("获取访问Token异常", e); + } + } + + /** + * 刷新访问Token + */ + private String refreshAccessToken(String refreshToken) { + try { + // 构建刷新Token的请求体 + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + Map body = new HashMap<>(); + body.put("RefreshToken", refreshToken); + + HttpEntity> request = new HttpEntity<>(body, headers); + + // 发送刷新Token的请求 + ResponseEntity response = restTemplate.postForEntity( + refreshUrl, + request, + Map.class + ); + + if (response.getStatusCode() == HttpStatus.OK) { + Map tokenResponse = response.getBody(); + if (tokenResponse != null && tokenResponse.get("Status").equals(0)) { + String accessToken = (String) tokenResponse.get("Token"); + String newRefreshToken = (String) tokenResponse.get("RefreshToken"); + Integer expireIn = (Integer) tokenResponse.get("ExpireIn"); + + // 更新缓存的Token信息 + TokenInfo tokenInfo = tokenCache.get(appId); + if (tokenInfo != null) { + tokenInfo.setAccessToken(accessToken); + tokenInfo.setRefreshToken(newRefreshToken); + tokenInfo.setExpireTime(System.currentTimeMillis() + (expireIn - 60) * 1000); + } + + log.info("成功刷新访问Token,有效期: {}秒", expireIn); + return accessToken; + } + } + + log.warn("刷新Token失败,将重新获取"); + return null; + } catch (Exception e) { + log.warn("刷新Token异常: {}", e.getMessage()); + return null; + } + } + + /** + * 检查Token是否已过期 + */ + private boolean isTokenExpired(TokenInfo tokenInfo) { + return tokenInfo.getExpireTime() < System.currentTimeMillis(); + } + + /** + * 构建请求参数 + */ + private Map buildRequestParams(int pageNum, int pageSize) { + Map requestBody = new HashMap<>(); + requestBody.put("PageSize", pageSize); + requestBody.put("PageNum", pageNum); + requestBody.put("OrFirst", true); + requestBody.put("Filters", Collections.emptyList()); // 空过滤器 + requestBody.put("OrderKey", "StartTime"); + requestBody.put("Desc", 1); // 降序排列 + + return requestBody; + } +// private Map buildRequestParams(int pageNum, int pageSize) { +// Map requestBody = new HashMap<>(); +// requestBody.put("'PageSize'", pageSize); +// requestBody.put("'PageNum'", pageNum); +// +// List>> filters = new ArrayList<>(); +// requestBody.put("'Filters'", filters); +// +// requestBody.put("'OrderKey'", "'StartTime'"); +// requestBody.put("'Desc'", "1"); +// requestBody.put("'OrFirst'", true); +// +// return requestBody; +// } + + /** + * Token信息内部类(修改为可更新的类) + */ + private static class TokenInfo { + private String accessToken; + private String refreshToken; + private long expireTime; + + public TokenInfo(String accessToken, String refreshToken, long expireTime) { + this.accessToken = accessToken; + this.refreshToken = refreshToken; + this.expireTime = expireTime; + } + + public String getAccessToken() { + return accessToken; + } + + public String getRefreshToken() { + return refreshToken; + } + + public long getExpireTime() { + return expireTime; + } + + public void setAccessToken(String accessToken) { + this.accessToken = accessToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + + public void setExpireTime(long expireTime) { + this.expireTime = expireTime; + } + } } diff --git a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/task/DataSyncTask.java b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/task/DataSyncTask.java index a2b8b30..f4e725d 100644 --- a/ruoyi-modules/Sis/src/main/java/org/dromara/sis/task/DataSyncTask.java +++ b/ruoyi-modules/Sis/src/main/java/org/dromara/sis/task/DataSyncTask.java @@ -69,7 +69,7 @@ public class DataSyncTask { // 提取操作记录数据 Map>> operatorMap = new HashMap<>(); for (Map recordMap : recordList) { - Long recordId = (Long) recordMap.get("Id"); + Long recordId = Long.valueOf((Integer)recordMap.get("Id")); List> operators = (List>) recordMap.get("TaskOperators"); if (operators != null && !operators.isEmpty()) { operatorMap.put(recordId, operators); diff --git a/ruoyi-modules/Sis/src/main/resources/application.yml b/ruoyi-modules/Sis/src/main/resources/application.yml index 739a643..0715e04 100644 --- a/ruoyi-modules/Sis/src/main/resources/application.yml +++ b/ruoyi-modules/Sis/src/main/resources/application.yml @@ -13,7 +13,10 @@ spring: # API 配置 api: url: https://norsos.lionking110.com/sos/v1/mntn/business/appId/alarm/list - + authUrl: https://norsos.lionking110.com/sos/v1/mntn/account/appId/token + refreshUrl: https://norsos.lionking110.com/sos/v1/mntn/account/refresh/token + appId: dfc7ec7507de4626b8c920c4fe1ff8b1 + appCode: fe11d05aa5704dffaa0b1c4b56ba80b2 # 同步配置 sync: pageSize: 100 diff --git a/ruoyi-visual/ruoyi-nacos/src/main/resources/application.properties b/ruoyi-visual/ruoyi-nacos/src/main/resources/application.properties index 9cc5f0d..22c1d9f 100644 --- a/ruoyi-visual/ruoyi-nacos/src/main/resources/application.properties +++ b/ruoyi-visual/ruoyi-nacos/src/main/resources/application.properties @@ -40,9 +40,9 @@ spring.sql.init.platform=mysql db.num=1 ### Connect URL of DB: -db.url.0=jdbc:mysql://127.0.0.1:3306/ry-config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true +db.url.0=jdbc:mysql://47.109.37.87:23306/ry-config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true db.user.0=root -db.password.0=root +db.password.0=admin@123456 ### the maximum retry times for push nacos.config.push.maxRetryTime=50