feat(biz): 实现动环系统HTTP客户端服务功能
- 新增园区、建筑及设备指标数据的获取方法 - 完善动环系统相关DTO类定义,包括分页数据结构 - 优化HTTP客户端的请求处理逻辑,增强错误处理机制 - 支持自动刷新访问令牌及请求重试机制 - 统一请求参数构建方式,提升代码可维护性
This commit is contained in:
@@ -1,7 +0,0 @@
|
|||||||
package com.jeelowcode.module.biz.dto;
|
|
||||||
|
|
||||||
// todo 待完善
|
|
||||||
public class PowerEnvBuildingDataDTO {
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package com.jeelowcode.module.biz.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动环系统建筑数据
|
||||||
|
*
|
||||||
|
* @author yangchenjj
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@Schema(description = "动环系统建筑数据")
|
||||||
|
public class PowerEnvBuildingItemDTO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建筑ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "建筑ID")
|
||||||
|
private String buildingId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 建筑名称
|
||||||
|
*/
|
||||||
|
@Schema(description = "建筑名称")
|
||||||
|
private String buildingName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 园区ID
|
||||||
|
*/
|
||||||
|
@Schema(description = "园区ID")
|
||||||
|
private String campusId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 园区名称
|
||||||
|
*/
|
||||||
|
@Schema(description = "园区名称")
|
||||||
|
private String campusName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态(0:停用,1:启用)
|
||||||
|
*/
|
||||||
|
@Schema(description = "状态(0:停用,1:启用)")
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package com.jeelowcode.module.biz.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
// todo 待完善
|
|
||||||
@Data
|
|
||||||
public class PowerEnvCampusDataDTO {
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.jeelowcode.module.biz.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动环系统园区信息
|
||||||
|
*
|
||||||
|
* @author yangchenjj
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@Schema(description = "动环系统园区信息")
|
||||||
|
public class PowerEnvCampusItemDTO {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 园区编号
|
||||||
|
*/
|
||||||
|
@Schema(description = "园区编号")
|
||||||
|
private String campusId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 园区名称
|
||||||
|
*/
|
||||||
|
@Schema(description = "园区名称")
|
||||||
|
private String campusName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态(0:停用,1:启用)
|
||||||
|
*/
|
||||||
|
@Schema(description = "状态(0:停用,1:启用)")
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
package com.jeelowcode.module.biz.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动环系统园区数据DTO
|
||||||
|
*
|
||||||
|
* @author yangchenjj
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@Schema(description = "动环系统园区数据DTO")
|
||||||
|
public class PowerEnvPageDataDTO<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总页数
|
||||||
|
*/
|
||||||
|
@Schema(description = "总页数")
|
||||||
|
private Integer totalPage = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每页数量
|
||||||
|
*/
|
||||||
|
@Schema(description = "每页数量")
|
||||||
|
private Integer pageSize = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当前页码
|
||||||
|
*/
|
||||||
|
@Schema(description = "当前页码")
|
||||||
|
private Integer currentPage = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总记录数
|
||||||
|
*/
|
||||||
|
@Schema(description = "总记录数")
|
||||||
|
private Integer totalCount = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据项
|
||||||
|
*/
|
||||||
|
@Schema(description = "数据项")
|
||||||
|
private List<T> items;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.jeelowcode.module.biz.http;
|
package com.jeelowcode.module.biz.http;
|
||||||
|
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.jeelowcode.module.biz.dto.PowerEnvRequestParamsDTO;
|
import com.jeelowcode.module.biz.dto.PowerEnvRequestParamsDTO;
|
||||||
import com.jeelowcode.module.biz.dto.PowerEnvResponseDataDTO;
|
import com.jeelowcode.module.biz.dto.PowerEnvResponseDataDTO;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -9,6 +10,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|||||||
import org.springframework.core.ParameterizedTypeReference;
|
import org.springframework.core.ParameterizedTypeReference;
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.client.HttpClientErrorException;
|
import org.springframework.web.client.HttpClientErrorException;
|
||||||
@@ -17,23 +19,35 @@ import org.springframework.web.client.RestTemplate;
|
|||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 可重试的HttpClient,支持自动登录和token刷新
|
* 可重试的HttpClient,支持自动登录和token刷新
|
||||||
* 线程安全的实现,可以在并发环境下使用
|
* 线程安全的实现,可以在并发环境下使用
|
||||||
|
*
|
||||||
|
* @author yangchenjj
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
@ConditionalOnProperty(name = "jeelowcode.powerenv.baseurl")
|
@ConditionalOnProperty(name = "jeelowcode.powerenv.baseurl")
|
||||||
public class RetryableHttpClient {
|
public class RetryableHttpClient {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动环系统基础URL
|
||||||
|
*/
|
||||||
@Value("${jeelowcode.powerenv.baseurl}")
|
@Value("${jeelowcode.powerenv.baseurl}")
|
||||||
private String baseUrl;
|
private String baseUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动环系统应用Key
|
||||||
|
*/
|
||||||
@Value("${jeelowcode.powerenv.appKey:campusT0Yoh1U6tXD3ZVXy}")
|
@Value("${jeelowcode.powerenv.appKey:campusT0Yoh1U6tXD3ZVXy}")
|
||||||
private String appKey;
|
private String appKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动环系统应用密钥
|
||||||
|
*/
|
||||||
@Value("${jeelowcode.powerenv.appSecret:3d4d89b5-8a14-4b83-a9aa-715bdc8264a1}")
|
@Value("${jeelowcode.powerenv.appSecret:3d4d89b5-8a14-4b83-a9aa-715bdc8264a1}")
|
||||||
private String appSecret;
|
private String appSecret;
|
||||||
|
|
||||||
@@ -62,12 +76,10 @@ public class RetryableHttpClient {
|
|||||||
/**
|
/**
|
||||||
* 发送GET请求并获取响应数据
|
* 发送GET请求并获取响应数据
|
||||||
*
|
*
|
||||||
* @param apiPath API接口路径
|
|
||||||
* @param requestParams 请求参数
|
* @param requestParams 请求参数
|
||||||
* @return 响应数据
|
* @return 响应数据
|
||||||
*/
|
*/
|
||||||
public ResponseEntity<PowerEnvResponseDataDTO<?>> getData(
|
public ResponseEntity<PowerEnvResponseDataDTO<?>> getData(PowerEnvRequestParamsDTO<?> requestParams) {
|
||||||
String apiPath, PowerEnvRequestParamsDTO<?> requestParams) {
|
|
||||||
// 检查令牌是否过期,如果过期则刷新
|
// 检查令牌是否过期,如果过期则刷新
|
||||||
if (isTokenExpired()) {
|
if (isTokenExpired()) {
|
||||||
refreshAccessToken();
|
refreshAccessToken();
|
||||||
@@ -75,18 +87,49 @@ public class RetryableHttpClient {
|
|||||||
// 构建带access_token参数的URL
|
// 构建带access_token参数的URL
|
||||||
String url = baseUrl + API_URL + "?access_token=" + accessToken;
|
String url = baseUrl + API_URL + "?access_token=" + accessToken;
|
||||||
try {
|
try {
|
||||||
return restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(requestParams),
|
// 请求数据
|
||||||
|
ResponseEntity<PowerEnvResponseDataDTO<?>> response = restTemplate
|
||||||
|
.exchange(url, HttpMethod.POST, new HttpEntity<>(requestParams),
|
||||||
new ParameterizedTypeReference<PowerEnvResponseDataDTO<?>>() {
|
new ParameterizedTypeReference<PowerEnvResponseDataDTO<?>>() {
|
||||||
});
|
});
|
||||||
} catch (HttpClientErrorException.Unauthorized e) {
|
|
||||||
log.warn("API请求未授权,尝试刷新访问令牌后重试");
|
// 检查响应状态码
|
||||||
// 如果是401未授权错误,则刷新令牌后重试
|
if (!Objects.equals(response.getStatusCode(), HttpStatus.OK)) {
|
||||||
|
// 如果不是200,则抛出异常
|
||||||
|
throw new HttpClientErrorException(response.getStatusCode(), "API请求失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果请求返回200,则检查响应内容
|
||||||
|
PowerEnvResponseDataDTO<?> responseData = response.getBody();
|
||||||
|
if (!Objects.requireNonNull(responseData).getSuccess()) {
|
||||||
|
// 如果响应消息为false,则还需要进一步查看错误信息
|
||||||
|
if (StrUtil.equalsAny(responseData.getCode(), "OPEN_API_PARAM_ERROR", "ACCESS_TOKEN_ERROR")) {
|
||||||
|
// 需要刷新Token并且重试
|
||||||
refreshAccessToken();
|
refreshAccessToken();
|
||||||
url = baseUrl + API_URL + "?access_token=" + accessToken;
|
url = baseUrl + API_URL + "?access_token=" + accessToken;
|
||||||
return restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(requestParams),
|
|
||||||
|
// 获得重试结果
|
||||||
|
ResponseEntity<PowerEnvResponseDataDTO<?>> retryResponse =
|
||||||
|
restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(requestParams),
|
||||||
new ParameterizedTypeReference<PowerEnvResponseDataDTO<?>>() {
|
new ParameterizedTypeReference<PowerEnvResponseDataDTO<?>>() {
|
||||||
});
|
});
|
||||||
|
if (Objects.equals(retryResponse.getStatusCode(), HttpStatus.OK) &&
|
||||||
|
Objects.requireNonNull(retryResponse.getBody()).getSuccess()) {
|
||||||
|
// 只放请求成功的结果
|
||||||
|
return retryResponse;
|
||||||
|
} else {
|
||||||
|
// 重试如果除了任何异常,则抛出异常
|
||||||
|
throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, responseData.getMsg());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 直接抛出异常
|
||||||
|
throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, responseData.getMsg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果成功了,则直接返回响应数据
|
||||||
|
return response;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
// 如果发生异常,则在日志中记录异常,并且抛出
|
||||||
log.error("请求API时发生异常", e);
|
log.error("请求API时发生异常", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,36 @@
|
|||||||
package com.jeelowcode.module.biz.service;
|
package com.jeelowcode.module.biz.service;
|
||||||
|
|
||||||
|
import com.jeelowcode.module.biz.dto.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务HTTP客户端服务接口
|
* 动环系统客户端服务接口
|
||||||
*
|
*
|
||||||
* @author lingma
|
* @author yangchenjj
|
||||||
*/
|
*/
|
||||||
public interface IBizHttpClientService {
|
public interface IBizHttpClientService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取园区列表
|
||||||
|
*
|
||||||
|
* @param params 园区查询参数
|
||||||
|
* @return 园区数据列表
|
||||||
|
*/
|
||||||
|
PowerEnvPageDataDTO<PowerEnvCampusItemDTO> listCampus(PowerEnvCampusParamsDTO params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取建筑列表
|
||||||
|
*
|
||||||
|
* @param params 建筑查询参数
|
||||||
|
* @return 建筑数据列表
|
||||||
|
*/
|
||||||
|
PowerEnvPageDataDTO<PowerEnvBuildingItemDTO> listBuilding(PowerEnvBuildingParamsDTO params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取设备指标数据列表
|
||||||
|
*
|
||||||
|
* @param deviceId 设备ID
|
||||||
|
* @return 设备指标数据列表
|
||||||
|
*/
|
||||||
|
PowerEnvDeviceDataDTO listDeviceMetrics(String deviceId);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
package com.jeelowcode.module.biz.service.impl;
|
package com.jeelowcode.module.biz.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import cn.hutool.core.convert.Convert;
|
||||||
|
import cn.hutool.core.lang.TypeReference;
|
||||||
|
import com.jeelowcode.module.biz.dto.*;
|
||||||
import com.jeelowcode.module.biz.http.RetryableHttpClient;
|
import com.jeelowcode.module.biz.http.RetryableHttpClient;
|
||||||
import com.jeelowcode.module.biz.service.IBizHttpClientService;
|
import com.jeelowcode.module.biz.service.IBizHttpClientService;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 业务HTTP客户端服务实现类
|
* 业务HTTP客户端服务实现类
|
||||||
@@ -19,4 +25,58 @@ public class BizHttpClientServiceImpl implements IBizHttpClientService {
|
|||||||
@Resource
|
@Resource
|
||||||
private RetryableHttpClient retryableHttpClient;
|
private RetryableHttpClient retryableHttpClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取园区分页数据
|
||||||
|
*
|
||||||
|
* @param params 园区查询参数
|
||||||
|
* @return 园区分页数据
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public PowerEnvPageDataDTO<PowerEnvCampusItemDTO> listCampus(PowerEnvCampusParamsDTO params) {
|
||||||
|
PowerEnvResponseDataDTO<?> response = retryableHttpClient.getData(
|
||||||
|
PowerEnvRequestParamsDTO.buildCampusRequestParams(params)).getBody();
|
||||||
|
return Optional.ofNullable(response)
|
||||||
|
.map(PowerEnvResponseDataDTO::getContent)
|
||||||
|
.map(content -> Convert.convert(
|
||||||
|
new TypeReference<PowerEnvPageDataDTO<PowerEnvCampusItemDTO>>() {
|
||||||
|
}, content))
|
||||||
|
.orElse(new PowerEnvPageDataDTO<PowerEnvCampusItemDTO>().setItems(Collections.emptyList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取建筑分页数据
|
||||||
|
*
|
||||||
|
* @param params 建筑查询参数
|
||||||
|
* @return 建筑分页数据
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public PowerEnvPageDataDTO<PowerEnvBuildingItemDTO> listBuilding(PowerEnvBuildingParamsDTO params) {
|
||||||
|
PowerEnvResponseDataDTO<?> response = retryableHttpClient.getData(
|
||||||
|
PowerEnvRequestParamsDTO.buildBuildingRequestParams(params)).getBody();
|
||||||
|
return Optional.ofNullable(response)
|
||||||
|
.map(PowerEnvResponseDataDTO::getContent)
|
||||||
|
.map(content -> Convert.convert(
|
||||||
|
new TypeReference<PowerEnvPageDataDTO<PowerEnvBuildingItemDTO>>() {
|
||||||
|
}, content))
|
||||||
|
.orElse(new PowerEnvPageDataDTO<PowerEnvBuildingItemDTO>().setItems(Collections.emptyList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取设备指标数据
|
||||||
|
*
|
||||||
|
* @param deviceId 设备ID
|
||||||
|
* @return 设备指标数据
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public PowerEnvDeviceDataDTO listDeviceMetrics(String deviceId) {
|
||||||
|
PowerEnvResponseDataDTO<?> response = retryableHttpClient.getData(
|
||||||
|
PowerEnvRequestParamsDTO.buildDeviceRunDataRequestParams(deviceId)).getBody();
|
||||||
|
return Optional.ofNullable(response)
|
||||||
|
.map(PowerEnvResponseDataDTO::getContent)
|
||||||
|
.map(content -> BeanUtil.toBean(content, PowerEnvDeviceDataDTO.class))
|
||||||
|
.orElse(new PowerEnvDeviceDataDTO().setDeviceUid(deviceId).setPropertyRunDataList(Collections.emptyList()));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user