From c3467add0e16ba58943903f35a381ebba2ae5f02 Mon Sep 17 00:00:00 2001 From: yjj38 Date: Fri, 11 Jul 2025 10:59:48 +0800 Subject: [PATCH] =?UTF-8?q?refactor(device):=20=E9=87=8D=E6=9E=84=20ArmClo?= =?UTF-8?q?udApiClient=20=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 格式化代码,提高可读性 - 优化 JSON 构建和解析逻辑 - 统一异常处理方式 - 简化部分代码结构,提高维护性 --- .../retention/device/ArmCloudApiClient.java | 398 +++++++++--------- app/src/main/jniLibs/arm64-v8a/libnative.so | Bin 81488 -> 81488 bytes 2 files changed, 202 insertions(+), 196 deletions(-) diff --git a/app/src/main/java/com/example/retention/device/ArmCloudApiClient.java b/app/src/main/java/com/example/retention/device/ArmCloudApiClient.java index 7918a06..cf65943 100644 --- a/app/src/main/java/com/example/retention/device/ArmCloudApiClient.java +++ b/app/src/main/java/com/example/retention/device/ArmCloudApiClient.java @@ -149,100 +149,100 @@ public class ArmCloudApiClient { * null, // settingProps oaidProps ); */ public String updateInstanceProperties( - String[] padCodes, - List modemPersistProps, - List modemProps, - List systemPersistProps, - List systemProps, - List settingProps, - List oaidProps + String[] padCodes, + List modemPersistProps, + List modemProps, + List systemPersistProps, + List systemProps, + List settingProps, + List oaidProps ) throws IOException { - if (padCodes == null || padCodes.length == 0) { - throw new IllegalArgumentException("padCodes 不能为空"); + if (padCodes == null || padCodes.length == 0) { + throw new IllegalArgumentException("padCodes 不能为空"); + } + + // 检查 padCodes 是否有重复项 + Set padCodeSet = new HashSet<>(); + for (String code : padCodes) { + if (!padCodeSet.add(code)) { + throw new IllegalArgumentException("padCodes 包含重复项: " + code); + } + } + + JSONObject json = new JSONObject(); + try { + json.put("padCodes", new JSONArray(Arrays.asList(padCodes))); + putPropertyItems(json, "modemPersistPropertiesList", modemPersistProps); + putPropertyItems(json, "modemPropertiesList", modemProps); + putPropertyItems(json, "systemPersistPropertiesList", systemPersistProps); + putPropertyItems(json, "systemPropertiesList", systemProps); + putPropertyItems(json, "settingPropertiesList", settingProps); + putPropertyItems(json, "oaidPropertiesList", oaidProps); + + } catch (JSONException e) { + LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: JSON 构建失败", e); + throw new IOException("JSON 构建失败", e); + } + + String jsonBody = json.toString(); + String timestamp = String.valueOf(System.currentTimeMillis()); + String API_PATH = "/openapi/open/pad/updatePadProperties"; + + if (secretKey == null || secretKey.isEmpty()) { + throw new IllegalArgumentException("secretKey 不能为空"); + } + + String signature; + try { + signature = calculateSignature(timestamp, API_PATH, jsonBody, secretKey); + } catch (Exception e) { + LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: 签名计算失败", e); + throw new IOException("签名计算失败", e); + } + + RequestBody body = RequestBody.create( + MediaType.get("application/json; charset=utf-8"), + jsonBody + ); + + Request request = new Request.Builder() + .url(baseUrl + API_PATH) + .addHeader("authver", "2.0") + .addHeader("x-ak", accessKey) + .addHeader("x-timestamp", timestamp) + .addHeader("x-sign", signature) + .post(body) + .build(); + + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) { + throw new IOException("请求失败: " + response); } - // 检查 padCodes 是否有重复项 - Set padCodeSet = new HashSet<>(); - for (String code : padCodes) { - if (!padCodeSet.add(code)) { - throw new IllegalArgumentException("padCodes 包含重复项: " + code); - } + ResponseBody responseBody = response.body(); + if (responseBody == null) { + throw new IOException("响应体为空"); } - JSONObject json = new JSONObject(); - try { - json.put("padCodes", new JSONArray(Arrays.asList(padCodes))); - putPropertyItems(json, "modemPersistPropertiesList", modemPersistProps); - putPropertyItems(json, "modemPropertiesList", modemProps); - putPropertyItems(json, "systemPersistPropertiesList", systemPersistProps); - putPropertyItems(json, "systemPropertiesList", systemProps); - putPropertyItems(json, "settingPropertiesList", settingProps); - putPropertyItems(json, "oaidPropertiesList", oaidProps); + String responseBodyString = responseBody.string(); + JSONObject responseJson = new JSONObject(responseBodyString); - } catch (JSONException e) { - LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: JSON 构建失败", e); - throw new IOException("JSON 构建失败", e); + // 校验返回码 + if (responseJson.has("code")) { + int code = responseJson.getInt("code"); + if (code != 200) { + String errorMsg = responseJson.optString("msg", "未知错误"); + LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: 接口返回错误码 " + code + ", 错误信息: " + errorMsg, null); + } + } else { + LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: 响应中缺少 'code' 字段", null); } - String jsonBody = json.toString(); - String timestamp = String.valueOf(System.currentTimeMillis()); - String API_PATH = "/openapi/open/pad/updatePadProperties"; - - if (secretKey == null || secretKey.isEmpty()) { - throw new IllegalArgumentException("secretKey 不能为空"); - } - - String signature; - try { - signature = calculateSignature(timestamp, API_PATH, jsonBody, secretKey); - } catch (Exception e) { - LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: 签名计算失败", e); - throw new IOException("签名计算失败", e); - } - - RequestBody body = RequestBody.create( - MediaType.get("application/json; charset=utf-8"), - jsonBody - ); - - Request request = new Request.Builder() - .url(baseUrl + API_PATH) - .addHeader("authver", "2.0") - .addHeader("x-ak", accessKey) - .addHeader("x-timestamp", timestamp) - .addHeader("x-sign", signature) - .post(body) - .build(); - - try (Response response = client.newCall(request).execute()) { - if (!response.isSuccessful()) { - throw new IOException("请求失败: " + response); - } - - ResponseBody responseBody = response.body(); - if (responseBody == null) { - throw new IOException("响应体为空"); - } - - String responseBodyString = responseBody.string(); - JSONObject responseJson = new JSONObject(responseBodyString); - - // 校验返回码 - if (responseJson.has("code")) { - int code = responseJson.getInt("code"); - if (code != 200) { - String errorMsg = responseJson.optString("msg", "未知错误"); - LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: 接口返回错误码 " + code + ", 错误信息: " + errorMsg, null); - } - } else { - LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: 响应中缺少 'code' 字段",null); - } - - return responseBodyString; - } catch (JSONException e) { - LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: 响应解析失败", e); - throw new IOException("响应解析失败", e); - } + return responseBodyString; + } catch (JSONException e) { + LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "updateInstanceProperties: 响应解析失败", e); + throw new IOException("响应解析失败", e); + } } @@ -288,118 +288,124 @@ public class ArmCloudApiClient { * @return 匹配的 padCode 数组 * @throws IOException 请求失败或网络错误 */ - public String[] getInstanceListInfo( - int page, - int rows, - String armServerCode, - String deviceCode, - String[] padCodes, - Integer[] groupIds, - String idc) throws IOException { + public String[] getInstanceListInfo( + int page, + int rows, + String armServerCode, + String deviceCode, + String[] padCodes, + Integer[] groupIds, + String idc) throws IOException { - JSONObject json = new JSONObject(); - try { - json.put("page", page); - json.put("rows", rows); + JSONObject json = new JSONObject(); + try { + json.put("page", page); + json.put("rows", rows); - if (armServerCode != null) json.put("armServerCode", armServerCode); - if (deviceCode != null) json.put("deviceCode", deviceCode); - if (padCodes != null && padCodes.length > 0) { - json.put("padCodes", new JSONArray(Arrays.asList(padCodes))); - } - if (groupIds != null && groupIds.length > 0) { - json.put("groupIds", new JSONArray(Arrays.asList(groupIds))); - } - if (idc != null) json.put("idc", idc); - - } catch (JSONException e) { - LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: JSON 构建失败", e); - throw new IOException("JSON 构建失败", e); - } - - String jsonBody = json.toString(); - String timestamp = String.valueOf(System.currentTimeMillis()); - String API_PATH = "/openapi/open/pad/infos"; - - if (secretKey == null || secretKey.isEmpty()) { - throw new IllegalArgumentException("secretKey 不能为空"); - } - - String signature; - try { - signature = calculateSignature(timestamp, API_PATH, jsonBody, secretKey); - } catch (Exception e) { - LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: 签名计算失败", e); - throw new IOException("签名计算失败", e); - } - - RequestBody body = RequestBody.create( - MediaType.get("application/json; charset=utf-8"), - jsonBody - ); - - Request request = new Request.Builder() - .url(baseUrl + API_PATH) - .addHeader("authver", "2.0") - .addHeader("x-ak", accessKey) - .addHeader("x-timestamp", timestamp) - .addHeader("x-sign", signature) - .post(body) - .build(); - - try (Response response = client.newCall(request).execute()) { - if (!response.isSuccessful()) { - throw new IOException("请求失败: " + response); - } - - ResponseBody responseBody = response.body(); - if (responseBody == null) { - throw new IOException("响应体为空"); - } - - String responseBodyString = responseBody.string(); - JSONObject responseJson = new JSONObject(responseBodyString); - - // 校验返回码 - if (responseJson.has("code")) { - int code = responseJson.getInt("code"); - if (code != 200) { - String errorMsg = responseJson.optString("msg", "未知错误"); - LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: 接口返回错误码 " + code + ", 错误信息: " + errorMsg, null); - throw new IOException("接口返回错误码: " + code + ", 错误信息: " + errorMsg); - } - } else { - LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: 响应中缺少 'code' 字段", null); - throw new IOException("响应中缺少 'code' 字段"); - } - - // 提取 padCode 列表 - JSONArray pageDataArray = responseJson.optJSONObject("data") - .optJSONArray("pageData"); - - if (pageDataArray == null || pageDataArray.length() == 0) { - LogFileUtil.logAndWrite(Log.WARN, "ArmCloudApiClient", "getInstanceListInfo: 查询结果为空", null); - return new String[0]; - } - - int length = pageDataArray.length(); - String[] padCodesResult = new String[length]; - - for (int i = 0; i < length; i++) { - JSONObject item = pageDataArray.getJSONObject(i); - if (!item.has("padCode")) { - LogFileUtil.logAndWrite(Log.WARN, "ArmCloudApiClient", "getInstanceListInfo: 返回对象缺少 'padCode' 字段", null); - throw new IOException("返回对象缺少 'padCode' 字段"); - } - padCodesResult[i] = item.getString("padCode"); - } - - return padCodesResult; - - } catch (JSONException e) { - LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: 响应解析失败", e); - throw new IOException("响应解析失败", e); - } + if (armServerCode != null) { + json.put("armServerCode", armServerCode); + } + if (deviceCode != null) { + json.put("deviceCode", deviceCode); + } + if (padCodes != null && padCodes.length > 0) { + json.put("padCodes", new JSONArray(Arrays.asList(padCodes))); + } + if (groupIds != null && groupIds.length > 0) { + json.put("groupIds", new JSONArray(Arrays.asList(groupIds))); + } + if (idc != null) { + json.put("idc", idc); } + } catch (JSONException e) { + LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: JSON 构建失败", e); + throw new IOException("JSON 构建失败", e); + } + + String jsonBody = json.toString(); + String timestamp = String.valueOf(System.currentTimeMillis()); + String API_PATH = "/openapi/open/pad/infos"; + + if (secretKey == null || secretKey.isEmpty()) { + throw new IllegalArgumentException("secretKey 不能为空"); + } + + String signature; + try { + signature = calculateSignature(timestamp, API_PATH, jsonBody, secretKey); + } catch (Exception e) { + LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: 签名计算失败", e); + throw new IOException("签名计算失败", e); + } + + RequestBody body = RequestBody.create( + MediaType.get("application/json; charset=utf-8"), + jsonBody + ); + + Request request = new Request.Builder() + .url(baseUrl + API_PATH) + .addHeader("authver", "2.0") + .addHeader("x-ak", accessKey) + .addHeader("x-timestamp", timestamp) + .addHeader("x-sign", signature) + .post(body) + .build(); + + try (Response response = client.newCall(request).execute()) { + if (!response.isSuccessful()) { + throw new IOException("请求失败: " + response); + } + + ResponseBody responseBody = response.body(); + if (responseBody == null) { + throw new IOException("响应体为空"); + } + + String responseBodyString = responseBody.string(); + JSONObject responseJson = new JSONObject(responseBodyString); + + // 校验返回码 + if (responseJson.has("code")) { + int code = responseJson.getInt("code"); + if (code != 200) { + String errorMsg = responseJson.optString("msg", "未知错误"); + LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: 接口返回错误码 " + code + ", 错误信息: " + errorMsg, null); + throw new IOException("接口返回错误码: " + code + ", 错误信息: " + errorMsg); + } + } else { + LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: 响应中缺少 'code' 字段", null); + throw new IOException("响应中缺少 'code' 字段"); + } + + // 提取 padCode 列表 + JSONArray pageDataArray = responseJson.optJSONObject("data") + .optJSONArray("pageData"); + + if (pageDataArray == null || pageDataArray.length() == 0) { + LogFileUtil.logAndWrite(Log.WARN, "ArmCloudApiClient", "getInstanceListInfo: 查询结果为空", null); + return new String[0]; + } + + int length = pageDataArray.length(); + String[] padCodesResult = new String[length]; + + for (int i = 0; i < length; i++) { + JSONObject item = pageDataArray.getJSONObject(i); + if (!item.has("padCode")) { + LogFileUtil.logAndWrite(Log.WARN, "ArmCloudApiClient", "getInstanceListInfo: 返回对象缺少 'padCode' 字段", null); + throw new IOException("返回对象缺少 'padCode' 字段"); + } + padCodesResult[i] = item.getString("padCode"); + } + + return padCodesResult; + + } catch (JSONException e) { + LogFileUtil.logAndWrite(Log.ERROR, "ArmCloudApiClient", "getInstanceListInfo: 响应解析失败", e); + throw new IOException("响应解析失败", e); + } + } + } diff --git a/app/src/main/jniLibs/arm64-v8a/libnative.so b/app/src/main/jniLibs/arm64-v8a/libnative.so index 0630454ec86547a109d68fd6735efd5a36efcb9a..967a4181429ae43d9831fb0df1b8bcbdfa361116 100644 GIT binary patch delta 9884 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