Explorar o código

feat(electric): 实现电力平台Token获取与管理功能

- 新增获取Token常量 QUERY_TOKEN
- 修改OkHttpUtil工具类,支持返回JsonObject格式响应
- 添加Gson依赖用于JSON解析
- 创建QueryTokenParms、QueryTokenResponseData等请求响应实体类
- 完善TokenManager中的远程Token获取逻辑
- 实现完整的Token签名加密及接口调用流程
-优化Token存储与刷新机制的日志记录
wzq hai 4 semanas
pai
achega
457c572763

+ 7 - 0
pom.xml

@@ -69,6 +69,13 @@
             <scope>provided</scope>
         </dependency>
 
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.8.9</version>
+        </dependency>
+
+
         <dependency>
             <groupId>cn.hutool</groupId>
             <artifactId>hutool-all</artifactId>

+ 5 - 0
src/main/java/com/zsElectric/boot/common/constant/ConnectivityConstants.java

@@ -40,6 +40,11 @@ public interface ConnectivityConstants {
      * */
     String TEST_DOMAIN = "https://parking-test.gyzhtc.com/energy-interconnection/evcs/v1.1/1/MA6DP6BE7/";
 
+    /**
+     * 获取Token
+     * */
+    String QUERY_TOKEN = "/evcs/v1/query_token";
+
     /**
      * 查询业务策略信息
      * */

+ 53 - 28
src/main/java/com/zsElectric/boot/common/util/OkHttpUtil.java

@@ -1,5 +1,7 @@
 package com.zsElectric.boot.common.util;
 
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
 import okhttp3.*;
 import org.springframework.stereotype.Component;
 
@@ -15,90 +17,113 @@ public class OkHttpUtil {
     public OkHttpUtil(OkHttpClient okHttpClient) {
         this.okHttpClient = okHttpClient;
     }
-    
+
     /**
-     * 执行 GET 请求
+     * 执行 GET 请求,返回 JsonObject
      */
-    public String doGet(String url, Map<String, String> headers) throws IOException {
+    public JsonObject doGet(String url, Map<String, String> headers) throws IOException {
         Request.Builder requestBuilder = new Request.Builder().url(url);
-        
+
         if (headers != null) {
             headers.forEach(requestBuilder::addHeader);
         }
-        
+
         Request request = requestBuilder.build();
-        
+
         try (Response response = okHttpClient.newCall(request).execute()) {
             if (!response.isSuccessful()) {
                 throw new IOException("Unexpected code: " + response);
             }
-            return response.body().string();
+            String responseBody = response.body().string();
+            return JsonParser.parseString(responseBody).getAsJsonObject();
         }
     }
-    
+
     /**
-     * 执行 POST 请求(JSON 数据)
+     * 执行 POST 请求(JSON 数据),返回 JsonObject
      */
-    public String doPostJson(String url, String json, Map<String, String> headers) throws IOException {
+    public JsonObject doPostJson(String url, String json, Map<String, String> headers) throws IOException {
         RequestBody body = RequestBody.create(json, JSON);
-        
+
         Request.Builder requestBuilder = new Request.Builder()
                 .url(url)
                 .post(body);
-                
+
         if (headers != null) {
             headers.forEach(requestBuilder::addHeader);
         }
-        
+
         Request request = requestBuilder.build();
-        
+
         try (Response response = okHttpClient.newCall(request).execute()) {
             if (!response.isSuccessful()) {
                 throw new IOException("Unexpected code: " + response);
             }
-            return response.body().string();
+            String responseBody = response.body().string();
+            return JsonParser.parseString(responseBody).getAsJsonObject();
         }
     }
-    
+
     /**
-     * 执行 POST 请求(表单数据)
+     * 执行 POST 请求(表单数据),返回 JsonObject
      */
-    public String doPostForm(String url, Map<String, String> formData, Map<String, String> headers) throws IOException {
+    public JsonObject doPostForm(String url, Map<String, Object> formData, Map<String, String> headers) throws IOException {
         FormBody.Builder formBodyBuilder = new FormBody.Builder();
-        
+
         if (formData != null) {
-            formData.forEach(formBodyBuilder::add);
+            formData.forEach((k, v) -> formBodyBuilder.add(k, String.valueOf(v)));
         }
-        
+
         Request.Builder requestBuilder = new Request.Builder()
                 .url(url)
                 .post(formBodyBuilder.build());
-                
+
         if (headers != null) {
             headers.forEach(requestBuilder::addHeader);
         }
-        
+
         Request request = requestBuilder.build();
-        
+
         try (Response response = okHttpClient.newCall(request).execute()) {
             if (!response.isSuccessful()) {
                 throw new IOException("Unexpected code: " + response);
             }
-            return response.body().string();
+            String responseBody = response.body().string();
+            return JsonParser.parseString(responseBody).getAsJsonObject();
         }
     }
-    
+
     /**
      * 异步 GET 请求
      */
     public void doGetAsync(String url, Map<String, String> headers, Callback callback) {
         Request.Builder requestBuilder = new Request.Builder().url(url);
-        
+
         if (headers != null) {
             headers.forEach(requestBuilder::addHeader);
         }
-        
+
         Request request = requestBuilder.build();
         okHttpClient.newCall(request).enqueue(callback);
     }
+
+    /**
+     * 执行 GET 请求,返回字符串(保留原有方法)
+     */
+    public String doGetAsString(String url, Map<String, String> headers) throws IOException {
+        Request.Builder requestBuilder = new Request.Builder().url(url);
+
+        if (headers != null) {
+            headers.forEach(requestBuilder::addHeader);
+        }
+
+        Request request = requestBuilder.build();
+
+        try (Response response = okHttpClient.newCall(request).execute()) {
+            if (!response.isSuccessful()) {
+                throw new IOException("Unexpected code: " + response);
+            }
+            return response.body().string();
+        }
+    }
 }

+ 27 - 0
src/main/java/com/zsElectric/boot/common/util/electric/QueryTokenParms.java

@@ -0,0 +1,27 @@
+package com.zsElectric.boot.common.util.electric;
+
+import lombok.Data;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+@Accessors(chain = true)
+@ToString
+public class QueryTokenParms implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 运营商ID
+     */
+    private String OperatorID;
+
+    /**
+     * 运营商密钥
+     */
+    private String OperatorSecret;
+}

+ 39 - 0
src/main/java/com/zsElectric/boot/common/util/electric/QueryTokenResponseData.java

@@ -0,0 +1,39 @@
+package com.zsElectric.boot.common.util.electric;
+
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+public class QueryTokenResponseData implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 运营商ID
+     */
+    private String OperatorID;
+
+    /**
+     * 响应状态 0-成功,1-失败
+     */
+    private Integer SuccStat;
+
+    /**
+     * 访问令牌
+     */
+    private String AccessToken;
+
+    /**
+     * 令牌有效期,单位秒
+     */
+    private Integer TokenAvailableTime;
+
+    /**
+     * 错误原因 0-无,1-OperatorID无效
+     */
+    private Integer FailReason;
+
+}

+ 41 - 0
src/main/java/com/zsElectric/boot/common/util/electric/RequestParmsEntity.java

@@ -0,0 +1,41 @@
+package com.zsElectric.boot.common.util.electric;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serial;
+import java.io.Serializable;
+
+@Data
+@Accessors(chain = true)
+public class RequestParmsEntity implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 运营商标识
+     */
+    private String OperatorID;
+
+    /**
+     * 加密后的参数
+     */
+    private String Data;
+
+    /**
+     * 时间戳
+     */
+    private String TimeStamp;
+
+    /**
+     * 序列号
+     */
+    private String Seq;
+
+    /**
+     * 签名
+     */
+    private String Sig;
+
+}

+ 58 - 15
src/main/java/com/zsElectric/boot/common/util/electric/TokenManager.java

@@ -1,6 +1,15 @@
 package com.zsElectric.boot.common.util.electric;
 
+import cn.hutool.core.bean.BeanUtil;
+import com.google.gson.Gson;
 import com.google.gson.JsonObject;
+import com.zsElectric.boot.common.constant.ConnectivityConstants;
+import com.zsElectric.boot.common.util.AESCryptoUtil;
+import com.zsElectric.boot.common.util.HmacMD5Util;
+import com.zsElectric.boot.common.util.OkHttpUtil;
+import com.zsElectric.boot.common.util.SequenceGenUtil;
+import com.zsElectric.boot.core.exception.BusinessException;
+import jakarta.annotation.Resource;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.checkerframework.checker.units.qual.A;
@@ -13,12 +22,15 @@ import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantLock;
 
 /**
- * Token管理器 - 负责Token的获取、刷新、存储和有效性检查[2](@ref)
+ * Token管理器 - 负责Token的获取、刷新、存储和有效性检查
  */
 @Slf4j
 @Component
 @RequiredArgsConstructor
 public class TokenManager {
+
+    @Resource
+    private OkHttpUtil okHttpUtil;
     
     private static final String TOKEN_KEY = "api:local:accessToken";
     private static final String TOKEN_REFRESH_LOCK_KEY = "api:token:refresh:lock";
@@ -74,7 +86,7 @@ public class TokenManager {
         }
 
         try {
-            // 获取分布式锁,防止集群环境下多个实例同时刷新Token[2](@ref)
+            // 获取分布式锁,防止集群环境下多个实例同时刷新
             String lockKey = TOKEN_REFRESH_LOCK_KEY;
             Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(
                     lockKey, "LOCK", 30, TimeUnit.SECONDS);
@@ -135,21 +147,53 @@ public class TokenManager {
     }
 
     /**
-     * 调用第三方接口获取新Token[2](@ref)
+     * 调用第三方接口获取新Token
      */
     private ApiToken fetchNewTokenFromRemote() {
         try {
-            //todo 根据实际情况调用第三方API获取Token
-
-//            if (Objects.isNull(response) || !response.get) {
-//                log.error("调用第三方接口获取Token失败,apiKey: {}", apiKey);
-//                return null;
-//            }
+            //调用第三方API获取Token
+            QueryTokenParms queryTokenParms = new QueryTokenParms();
+            queryTokenParms
+                    .setOperatorID(ConnectivityConstants.OPERATOR_ID)
+                    .setOperatorSecret(ConnectivityConstants.OPERATOR_SECRET);
+
+            RequestParmsEntity requestParms = new RequestParmsEntity();
+            SequenceGenUtil.SequenceResult result = SequenceGenUtil.generate();
+
+            requestParms
+                    .setOperatorID(ConnectivityConstants.OPERATOR_ID)
+                    .setData(AESCryptoUtil.decrypt(queryTokenParms.toString(),ConnectivityConstants.DATA_SECRET,ConnectivityConstants.DATA_SECRET_IV))
+                    .setTimeStamp(result.getTimestamp())
+                    .setSeq(result.getSequence())
+                    .setSig(HmacMD5Util.genSign(requestParms.getOperatorID(),requestParms.getData(),requestParms.getTimeStamp(),requestParms.getSeq(),ConnectivityConstants.SIG_SECRET));
+
+            JsonObject response = okHttpUtil.doPostForm(ConnectivityConstants.QUERY_TOKEN, BeanUtil.beanToMap(requestParms), null);
+
+            if (Objects.isNull(response)) {
+                log.error("调用第三方接口获取Token失败");
+                return null;
+            }
+            Gson gson = new Gson();
+            QueryTokenResponseData responseData = gson.fromJson(response, QueryTokenResponseData.class);
+
+            if (responseData.getSuccStat() == 1){
+                //0-无,1-OperatorID无效
+                String failReason = "";
+                if (responseData.getFailReason() == 0){
+                    failReason = "无";
+                }
+                if(responseData.getFailReason() == 1){
+                    failReason = "OperatorID无效";
+                }
+                log.error("调用第三方接口获取Token失败,失败原因: {}", failReason);
+                return null;
+            }
 
             ApiToken apiToken = new ApiToken();
-//            apiToken.setAccessToken(response.getAccessToken());
-//            ApiToken.setTokenAvailableTime(response.getExpiresIn());
-//            apiToken.setExpiresIn(response.getExpiresIn());
+            apiToken.setAccessToken("Bearer ");
+            apiToken.setTokenAvailableTime(responseData.getTokenAvailableTime());
+            apiToken.setObtainTime(LocalDateTime.now());
+            apiToken.setExpireTime(apiToken.getObtainTime().plusSeconds(apiToken.getTokenAvailableTime()));
 
             return apiToken;
         } catch (Exception e) {
@@ -159,18 +203,17 @@ public class TokenManager {
     }
 
     /**
-     * 设置Token信息并存储到Redis[5](@ref)
+     * 设置Token信息并存储到Redis
      */
     private void setupAndStoreToken(String tokenKey, ApiToken token) {
         LocalDateTime obtainTime = LocalDateTime.now();
         token.setObtainTime(obtainTime);
         token.setExpireTime(obtainTime.plusSeconds(token.getTokenAvailableTime()));
-
         storeToken(tokenKey, token);
     }
 
     /**
-     * 存储Token到Redis[5](@ref)
+     * 存储Token到Redis
      */
     private void storeToken(String apiKey, ApiToken token) {
         long expireSeconds = Duration.between(LocalDateTime.now(), token.getExpireTime()).getSeconds();