SOS同步
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run

This commit is contained in:
mocheng 2025-07-28 01:01:25 +08:00
parent cd9227be31
commit 5274fb8d64
5 changed files with 268 additions and 53 deletions

View File

@ -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();
// }
//}

View File

@ -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<String, TokenInfo> tokenCache = new ConcurrentHashMap<>();
public ApiServiceImpl(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Override
@Retryable(value = {HttpClientErrorException.Unauthorized.class}, maxAttempts = 2, backoff = @Backoff(delay = 1000))
public Map<String, Object> 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<String, Object> requestBody = buildRequestParams(pageNum, pageSize);
log.info("请求参数:{}", requestBody);
try {
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
// 构建请求体
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("PageSize", pageSize);
requestBody.put("PageNum", pageNum);
List<List<Map<String, Object>>> 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<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
ResponseEntity<Map> 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<String, Object> 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<String, Object> body = new HashMap<>();
body.put("AppId", appId);
body.put("AppCode", appCode);
HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);
// 发送获取Token的请求
ResponseEntity<Map> response = restTemplate.postForEntity(
authUrl,
request,
Map.class
);
log.info("接口响应:{},状态码:{}",response,response.getStatusCode());
if (response.getStatusCode() == HttpStatus.OK) {
Map<String, Object> 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<String, Object> body = new HashMap<>();
body.put("RefreshToken", refreshToken);
HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);
// 发送刷新Token的请求
ResponseEntity<Map> response = restTemplate.postForEntity(
refreshUrl,
request,
Map.class
);
if (response.getStatusCode() == HttpStatus.OK) {
Map<String, Object> 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<String, Object> buildRequestParams(int pageNum, int pageSize) {
Map<String, Object> 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<String, Object> buildRequestParams(int pageNum, int pageSize) {
// Map<String, Object> requestBody = new HashMap<>();
// requestBody.put("'PageSize'", pageSize);
// requestBody.put("'PageNum'", pageNum);
//
// List<List<Map<String, Object>>> 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;
}
}
}

View File

@ -69,7 +69,7 @@ public class DataSyncTask {
// 提取操作记录数据
Map<Long, List<Map<String, Object>>> operatorMap = new HashMap<>();
for (Map<String, Object> recordMap : recordList) {
Long recordId = (Long) recordMap.get("Id");
Long recordId = Long.valueOf((Integer)recordMap.get("Id"));
List<Map<String, Object>> operators = (List<Map<String, Object>>) recordMap.get("TaskOperators");
if (operators != null && !operators.isEmpty()) {
operatorMap.put(recordId, operators);

View File

@ -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

View File

@ -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