feat(powerenv): 实现动环系统API集成和访问令牌管理
- 添加动环系统相关DTO类,包括访问令牌、建筑、园区、设备等数据传输对象 - 实现基于注解的条件化HTTP客户端服务,支持动态启用/禁用 - 集成可重试HTTP客户端,支持自动刷新访问令牌和异常重试 - 移除旧的RestTemplate配置类,统一通过Spring Boot自动配置管理 - 更新API调用方式,支持POST请求和结构化响应处理 - 添加应用密钥和密钥的外部化配置支持 - 完善动环系统接口参数封装和响应解析逻辑
This commit is contained in:
@@ -1,22 +0,0 @@
|
||||
package com.jeelowcode.module.biz.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* HttpClient配置类
|
||||
*
|
||||
* @author lingma
|
||||
*/
|
||||
@Configuration
|
||||
public class HttpClientConfig {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.jeelowcode.module.biz.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 动环系统访问令牌数据DTO
|
||||
*
|
||||
* @author yangchenjj
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(description = "动环系统访问令牌数据DTO")
|
||||
public class PowerEnvAccessTokenDataDTO {
|
||||
|
||||
/**
|
||||
* 访问令牌
|
||||
*/
|
||||
@JsonProperty("access_token")
|
||||
@Schema(description = "访问令牌")
|
||||
private String accessToken;
|
||||
|
||||
/**
|
||||
* 访问令牌过期时间
|
||||
*/
|
||||
@JsonProperty("expires_in")
|
||||
@Schema(description = "访问令牌过期时间")
|
||||
private Long expiresIn;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 动环系统建筑请求参数DTO
|
||||
*
|
||||
* @author yangchenjj
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(description = "动环系统建筑请求参数DTO")
|
||||
public class PowerEnvBuildingParamsDTO {
|
||||
|
||||
/**
|
||||
* 每页数量
|
||||
*/
|
||||
@Schema(description = "每页数量")
|
||||
private String pageSize;
|
||||
|
||||
/**
|
||||
* 页码
|
||||
*/
|
||||
@Schema(description = "页码")
|
||||
private String page;
|
||||
|
||||
/**
|
||||
* 园区编号
|
||||
*/
|
||||
@Schema(description = "园区编号")
|
||||
private String campusId;
|
||||
|
||||
/**
|
||||
* 关键字
|
||||
*/
|
||||
@Schema(description = "关键字")
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 状态(0:停用,1:启用)
|
||||
*/
|
||||
@Schema(description = "状态(0:停用,1:启用)")
|
||||
private String status;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.jeelowcode.module.biz.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
// todo 待完善
|
||||
@Data
|
||||
public class PowerEnvCampusDataDTO {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.jeelowcode.module.biz.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 动环系统园区请求参数DTO
|
||||
*
|
||||
* @author yangchenjj
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(description = "动环系统园区请求参数DTO")
|
||||
public class PowerEnvCampusParamsDTO {
|
||||
|
||||
/**
|
||||
* 每页数量
|
||||
*/
|
||||
@Schema(description = "每页数量")
|
||||
private String pageSize;
|
||||
|
||||
/**
|
||||
* 页码
|
||||
*/
|
||||
@Schema(description = "页码")
|
||||
private String page;
|
||||
|
||||
/**
|
||||
* 关键字
|
||||
*/
|
||||
@Schema(description = "关键字")
|
||||
private String key;
|
||||
|
||||
/**
|
||||
* 状态(0:停用,1:启用)
|
||||
*/
|
||||
@Schema(description = "状态(0:停用,1:启用)")
|
||||
private String status;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 动环系统设备信息数据
|
||||
*
|
||||
* @author yangchenjj
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(description = "动环系统设备信息数据")
|
||||
public class PowerEnvDeviceDataDTO {
|
||||
|
||||
/**
|
||||
* 设备唯一标识
|
||||
*/
|
||||
@Schema(description = "设备唯一标识")
|
||||
private String deviceUid;
|
||||
|
||||
/**
|
||||
* 设备运行数据列表
|
||||
*/
|
||||
@Schema(description = "设备运行数据列表")
|
||||
private List<PowerEnvDeviceMetricDataDTO> propertyRunDataList;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.jeelowcode.module.biz.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 动环系统设备运行数据DTO
|
||||
*
|
||||
* @author yangchenjj
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(description = "动环系统设备运行数据DTO")
|
||||
public class PowerEnvDeviceMetricDataDTO {
|
||||
|
||||
/**
|
||||
* 设备运行数据ID
|
||||
*/
|
||||
@Schema(description = "设备运行数据ID")
|
||||
private String metadataUid;
|
||||
|
||||
/**
|
||||
* 设备运行数据编码
|
||||
*/
|
||||
@Schema(description = "设备运行数据编码")
|
||||
private String metadataCode;
|
||||
/**
|
||||
* 设备运行数据名称
|
||||
*/
|
||||
@Schema(description = "设备运行数据名称")
|
||||
private String metadataName;
|
||||
/**
|
||||
* 设备运行数据属性编码
|
||||
*/
|
||||
@Schema(description = "设备运行数据属性编码")
|
||||
private String propertyCode;
|
||||
/**
|
||||
* 设备运行数据属性名称
|
||||
*/
|
||||
@Schema(description = "设备运行数据属性名称")
|
||||
private String propertyName;
|
||||
/**
|
||||
* 设备运行数据单位编码
|
||||
*/
|
||||
@Schema(description = "设备运行数据单位编码")
|
||||
private String unitCode;
|
||||
/**
|
||||
* 设备运行数据值类型
|
||||
*/
|
||||
@Schema(description = "设备运行数据值类型")
|
||||
private Integer valueType;
|
||||
/**
|
||||
* 设备运行数据更新时间
|
||||
*/
|
||||
@Schema(description = "设备运行数据更新时间")
|
||||
private Long updateTime;
|
||||
/**
|
||||
* 设备运行点位类型
|
||||
*/
|
||||
@Schema(description = "设备运行点位类型")
|
||||
private Integer type;
|
||||
/**
|
||||
* 设备运行数据值
|
||||
*/
|
||||
@Schema(description = "设备运行数据值")
|
||||
private String value;
|
||||
/**
|
||||
* 设备运行数据控制枚举值
|
||||
*/
|
||||
@Schema(description = "设备运行数据控制枚举值")
|
||||
private String controlEnumValue;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.jeelowcode.module.biz.dto;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 动环请求参数
|
||||
*
|
||||
* @param <P> 参数值
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(description = "动环系统请求参数DTO")
|
||||
public class PowerEnvRequestParamsDTO<P> {
|
||||
|
||||
/**
|
||||
* 获取校区列表接口地址
|
||||
*/
|
||||
private final static String API_LIST_CAMPUS = "/space/campus/list";
|
||||
|
||||
/**
|
||||
* 获取楼栋列表接口地址
|
||||
*/
|
||||
private final static String API_LIST_BUILDING = "/space/building/list";
|
||||
|
||||
/**
|
||||
* 获取设备运行数据接口地址
|
||||
*/
|
||||
private final static String API_LIST_DEVICE_RUN_DATA = "/device/device/getDeviceRunData";
|
||||
|
||||
/**
|
||||
* 接口地址
|
||||
*/
|
||||
@Schema(description = "接口地址")
|
||||
private String api;
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
@Schema(description = "参数值")
|
||||
private List<P> params;
|
||||
|
||||
/**
|
||||
* 构建请求参数对象
|
||||
*
|
||||
* @param api 接口地址
|
||||
* @param param 请求参数
|
||||
* @param <P> 参数类型
|
||||
* @return PowerEnvRequestParamsDTO<P> 请求参数对象
|
||||
*/
|
||||
public static <P> PowerEnvRequestParamsDTO<P> buildRequestParams(String api, P param) {
|
||||
return new PowerEnvRequestParamsDTO<P>().setApi(api).setParams(Lists.newArrayList(param));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建获取校区列表的请求参数对象
|
||||
*
|
||||
* @param param 校区参数
|
||||
* @return PowerEnvRequestParamsDTO<PowerEnvCampusParamsDTO> 请求参数对象
|
||||
*/
|
||||
public static PowerEnvRequestParamsDTO<PowerEnvCampusParamsDTO> buildCampusRequestParams(PowerEnvCampusParamsDTO param) {
|
||||
return buildRequestParams(API_LIST_CAMPUS, param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建获取楼栋列表的请求参数对象
|
||||
*
|
||||
* @param param 楼栋参数
|
||||
* @return PowerEnvRequestParamsDTO<PowerEnvBuildingParamsDTO> 请求参数对象
|
||||
*/
|
||||
public static PowerEnvRequestParamsDTO<PowerEnvBuildingParamsDTO> buildBuildingRequestParams(PowerEnvBuildingParamsDTO param) {
|
||||
return buildRequestParams(API_LIST_BUILDING, param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建获取设备运行数据的请求参数对象
|
||||
*
|
||||
* @param param 设备运行数据参数
|
||||
* @return PowerEnvRequestParamsDTO<String> 请求参数对象
|
||||
*/
|
||||
public static PowerEnvRequestParamsDTO<String> buildDeviceRunDataRequestParams(String param) {
|
||||
return buildRequestParams(API_LIST_DEVICE_RUN_DATA, param);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 动环系统接口响应体
|
||||
*
|
||||
* @author yangchenjj
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@Schema(description = "动环系统接口响应体")
|
||||
public class PowerEnvResponseDataDTO<T> {
|
||||
|
||||
/**
|
||||
* 响应消息
|
||||
*/
|
||||
@Schema(description = "响应消息")
|
||||
private String msg;
|
||||
/**
|
||||
* 响应码
|
||||
*/
|
||||
@Schema(description = "响应码")
|
||||
private String code;
|
||||
/**
|
||||
* 请求ID
|
||||
*/
|
||||
@Schema(description = "请求ID")
|
||||
private String requestId;
|
||||
/**
|
||||
* 是否成功
|
||||
*/
|
||||
@Schema(description = "是否成功")
|
||||
private Boolean success;
|
||||
/**
|
||||
* 响应内容
|
||||
*/
|
||||
@Schema(description = "响应内容")
|
||||
private List<T> content;
|
||||
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
package com.jeelowcode.module.biz.http;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.jeelowcode.module.biz.dto.PowerEnvRequestParamsDTO;
|
||||
import com.jeelowcode.module.biz.dto.PowerEnvResponseDataDTO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.client.HttpClientErrorException;
|
||||
@@ -20,23 +25,24 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@ConditionalOnProperty(
|
||||
name = "jeelowcode.powerenv.baseurl", matchIfMissing = true)
|
||||
@ConditionalOnProperty(name = "jeelowcode.powerenv.baseurl")
|
||||
public class RetryableHttpClient {
|
||||
|
||||
@Value("${jeelowcode.powerenv.baseurl}")
|
||||
private String baseUrl;
|
||||
|
||||
@Value("${jeelowcode.powerenv.appKey:campusT0Yoh1U6tXD3ZVXy}")
|
||||
private String appKey;
|
||||
|
||||
@Value("${jeelowcode.powerenv.appSecret:3d4d89b5-8a14-4b83-a9aa-715bdc8264a1}")
|
||||
private String appSecret;
|
||||
|
||||
// 访问令牌URL
|
||||
private static final String ACCESS_TOKEN_URL = "/_campus/open/accessToken.json";
|
||||
|
||||
// API接口URL
|
||||
private static final String API_URL = "/_campus/open/api/invoked.json";
|
||||
|
||||
// 应用凭证
|
||||
private static final String APP_KEY = "campusT0Yoh1U6tXD3ZVXy";
|
||||
private static final String APP_SECRET = "3d4d89b5-8a14-4b83-a9aa-715bdc8264a1";
|
||||
|
||||
// 存储访问令牌及相关信息
|
||||
private volatile String accessToken;
|
||||
private volatile long tokenExpireTime;
|
||||
@@ -44,7 +50,6 @@ public class RetryableHttpClient {
|
||||
// 用于保护令牌更新操作的锁
|
||||
private final ReentrantLock tokenLock = new ReentrantLock();
|
||||
|
||||
// RestTemplate实例
|
||||
@Resource
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@@ -55,28 +60,32 @@ public class RetryableHttpClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取API数据
|
||||
* 发送GET请求并获取响应数据
|
||||
*
|
||||
* @return ResponseEntity<String> 响应结果
|
||||
* @param apiPath API接口路径
|
||||
* @param requestParams 请求参数
|
||||
* @return 响应数据
|
||||
*/
|
||||
public ResponseEntity<String> getApiData() {
|
||||
public ResponseEntity<PowerEnvResponseDataDTO<?>> getData(
|
||||
String apiPath, PowerEnvRequestParamsDTO<?> requestParams) {
|
||||
// 检查令牌是否过期,如果过期则刷新
|
||||
if (isTokenExpired()) {
|
||||
refreshAccessToken();
|
||||
}
|
||||
|
||||
// 构建带access_token参数的URL
|
||||
String url = API_URL + "?access_token=" + accessToken;
|
||||
|
||||
String url = baseUrl + API_URL + "?access_token=" + accessToken;
|
||||
try {
|
||||
// 尝试请求API
|
||||
return restTemplate.getForEntity(url, String.class);
|
||||
return restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(requestParams),
|
||||
new ParameterizedTypeReference<PowerEnvResponseDataDTO<?>>() {
|
||||
});
|
||||
} catch (HttpClientErrorException.Unauthorized e) {
|
||||
log.warn("API请求未授权,尝试刷新访问令牌后重试");
|
||||
// 如果是401未授权错误,则刷新令牌后重试
|
||||
refreshAccessToken();
|
||||
url = API_URL + "?access_token=" + accessToken;
|
||||
return restTemplate.getForEntity(url, String.class);
|
||||
url = baseUrl + API_URL + "?access_token=" + accessToken;
|
||||
return restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(requestParams),
|
||||
new ParameterizedTypeReference<PowerEnvResponseDataDTO<?>>() {
|
||||
});
|
||||
} catch (Exception e) {
|
||||
log.error("请求API时发生异常", e);
|
||||
throw e;
|
||||
@@ -97,7 +106,7 @@ public class RetryableHttpClient {
|
||||
log.info("开始刷新访问令牌");
|
||||
|
||||
// 构造获取令牌的参数
|
||||
String tokenUrl = baseUrl + ACCESS_TOKEN_URL + "?appKey=" + APP_KEY + "&" + APP_SECRET;
|
||||
String tokenUrl = baseUrl + ACCESS_TOKEN_URL + "?appKey=" + appKey + "&" + appSecret;
|
||||
|
||||
try {
|
||||
// 请求访问令牌
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.jeelowcode.module.biz.service;
|
||||
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
/**
|
||||
* 业务HTTP客户端服务接口
|
||||
*
|
||||
@@ -9,10 +7,5 @@ import org.springframework.http.ResponseEntity;
|
||||
*/
|
||||
public interface IBizHttpClientService {
|
||||
|
||||
/**
|
||||
* 获取API数据
|
||||
*
|
||||
* @return ResponseEntity<String> 响应结果
|
||||
*/
|
||||
ResponseEntity<String> getApiData();
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package com.jeelowcode.module.biz.service.impl;
|
||||
|
||||
import com.jeelowcode.module.biz.http.RetryableHttpClient;
|
||||
import com.jeelowcode.module.biz.service.IBizHttpClientService;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@@ -13,14 +13,10 @@ import javax.annotation.Resource;
|
||||
* @author lingma
|
||||
*/
|
||||
@Service
|
||||
@ConditionalOnProperty(name = "jeelowcode.powerenv.baseurl")
|
||||
public class BizHttpClientServiceImpl implements IBizHttpClientService {
|
||||
|
||||
@Resource
|
||||
private RetryableHttpClient retryableHttpClient;
|
||||
|
||||
@Override
|
||||
public ResponseEntity<String> getApiData() {
|
||||
return retryableHttpClient.getApiData();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user