From fe214e689a641063f751559accd69d373a673db2 Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 4 Feb 2026 16:26:36 +0800 Subject: [PATCH] 1 --- .../service/impl/WPS365ApiServiceImpl.java | 221 +++++------------- 1 file changed, 64 insertions(+), 157 deletions(-) diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/WPS365ApiServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/WPS365ApiServiceImpl.java index b127b89..da34be0 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/WPS365ApiServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/WPS365ApiServiceImpl.java @@ -219,178 +219,85 @@ public class WPS365ApiServiceImpl implements IWPS365ApiService { @Override public JSONObject readAirSheetCells(String accessToken, String fileId, String worksheetId, String range) { + // 智能表格(AirSheet)与在线表格(KSheet)使用同一 API 基址;openapi.wps.cn/v7/airsheet 已 404,改用 api/v1 + openapi/airsheet(与 KSheet 路径风格一致) + String baseUrl = wps365Config.getApiBaseUrl(); + int sheetIdx = parseSheetIndex(worksheetId, fileId); + + // 方案1:与 KSheet 一致的路径 GET /openapi/airsheet/{fileId}/sheets/{sheetIdx}/cells?range=... try { - // WPS365 AirSheet API路径格式 - // 根据文档:https://open.wps.cn/documents/app-integration-dev/wps365/server/airsheet/worksheets/VbHZwButmh - // 注意:AirSheet中,fileId和worksheetId可能是同一个值,或者worksheetId是整数索引 - // 如果用户只提供了一个ID(fileId),则fileId和worksheetId使用同一个值 - String baseUrl = "https://openapi.wps.cn/v7"; - - // 根据WPS365官方文档,AirSheet的worksheet_id必须是整数 - // 如果worksheetId为空或"0",使用0(第一个工作表) - // 如果worksheetId与fileId相同,说明用户只配置了一个ID,尝试使用fileId作为worksheetId - String wsId; - if (worksheetId == null || worksheetId.trim().isEmpty() || "0".equals(worksheetId)) { - // 默认使用0(第一个工作表) - wsId = "0"; - } else if (worksheetId.equals(fileId)) { - // 如果worksheetId与fileId相同,说明用户只配置了一个ID(类似腾讯文档) - // 在AirSheet中,这个ID可能既是file_id也是worksheet_id - wsId = fileId; - } else { - // 使用提供的worksheetId(应该是整数) - wsId = worksheetId; - } - - // 根据官方文档:https://open.wps.cn/documents/app-integration-dev/wps365/server/airsheet/worksheets/VbHZwButmh - // 正确路径:https://openapi.wps.cn/v7/airsheet/{file_id}/worksheets - // 注意:路径中不需要 worksheet_id,只需要 file_id - try { - String url = baseUrl + "/airsheet/" + fileId + "/worksheets"; - - // 如果指定了range,添加range参数 - if (range != null && !range.trim().isEmpty()) { - url += "?range=" + java.net.URLEncoder.encode(range, "UTF-8"); - } - - // 如果指定了worksheetId,也可以作为查询参数传递(如果API支持) - if (worksheetId != null && !worksheetId.trim().isEmpty() && !worksheetId.equals("0") && !worksheetId.equals(fileId)) { - if (url.contains("?")) { - url += "&worksheet_id=" + java.net.URLEncoder.encode(worksheetId, "UTF-8"); - } else { - url += "?worksheet_id=" + java.net.URLEncoder.encode(worksheetId, "UTF-8"); - } - } - - log.debug("使用官方文档路径 - url: {}, fileId: {}, worksheetId: {}, range: {}", url, fileId, worksheetId, range); - return WPS365ApiUtil.httpRequest("GET", url, accessToken, null); - } catch (Exception e) { - log.debug("官方文档路径失败,尝试其他方案", e); - } - - // 尝试多种API路径格式(降级方案) - // 方案1: 尝试使用fileId作为worksheetId(如果用户只配置了一个ID) - if (wsId.equals("0") && !fileId.equals(worksheetId)) { - try { - String url = baseUrl + "/airsheet/" + fileId + "/worksheets/" + fileId; - if (range != null && !range.trim().isEmpty()) { - url += "?range=" + java.net.URLEncoder.encode(range, "UTF-8"); - } - log.debug("尝试方案1 - 使用fileId作为worksheetId - url: {}", url); - return WPS365ApiUtil.httpRequest("GET", url, accessToken, null); - } catch (Exception e) { - log.debug("方案1失败,尝试其他方案", e); - } - } - - // 方案2: 使用 /range_data 子路径(根据官方文档,这是读取区域数据的标准路径) - // 注意:range_data接口需要使用 row_from, row_to, col_from, col_to 参数,而不是 range=A1:B5 - try { - String url = baseUrl + "/airsheet/" + fileId + "/worksheets/" + wsId + "/range_data"; - if (range != null && !range.trim().isEmpty()) { - // 尝试解析A1:B5格式的range,转换为行列参数 - int[] rangeParams = parseRangeToRowCol(range); - if (rangeParams != null && rangeParams.length == 4) { - // 使用行列参数格式(row_from, row_to, col_from, col_to) - // 注意:WPS365的行列索引可能从0开始或从1开始,需要测试确认 - url += "?row_from=" + rangeParams[0] + "&row_to=" + rangeParams[1] - + "&col_from=" + rangeParams[2] + "&col_to=" + rangeParams[3]; - } else { - // 如果解析失败,尝试作为range参数传递 - url += "?range=" + java.net.URLEncoder.encode(range, "UTF-8"); - } - } - log.debug("尝试方案2 - 使用/range_data子路径 - url: {}", url); - return WPS365ApiUtil.httpRequest("GET", url, accessToken, null); - } catch (Exception e) { - log.debug("方案2失败,尝试其他方案", e); - } - - // 方案3: 使用 /values 子路径 - try { - String url = baseUrl + "/airsheet/" + fileId + "/worksheets/" + wsId + "/values"; - if (range != null && !range.trim().isEmpty()) { - url += "?range=" + java.net.URLEncoder.encode(range, "UTF-8"); - } - log.debug("尝试方案3 - 使用/values子路径 - url: {}", url); - return WPS365ApiUtil.httpRequest("GET", url, accessToken, null); - } catch (Exception e) { - log.debug("方案3失败,尝试其他方案", e); - } - - // 方案4: 基础路径(不带子路径) - String url = baseUrl + "/airsheet/" + fileId + "/worksheets/" + wsId; + String url = baseUrl + "/openapi/airsheet/" + fileId + "/sheets/" + sheetIdx + "/cells"; if (range != null && !range.trim().isEmpty()) { url += "?range=" + java.net.URLEncoder.encode(range, "UTF-8"); } - log.debug("尝试方案4 - 基础路径 - url: {}", url); - JSONObject result = WPS365ApiUtil.httpRequest("GET", url, accessToken, null); - return result; + log.debug("读取AirSheet - url: {}, fileId: {}, sheetIdx: {}, range: {}", url, fileId, sheetIdx, range); + return WPS365ApiUtil.httpRequest("GET", url, accessToken, null); } catch (Exception e) { - log.error("读取AirSheet数据失败 - fileId: {}, worksheetId: {}, range: {}", fileId, worksheetId, range, e); - // 如果失败,尝试使用 /values 子路径 - try { - String baseUrl = "https://openapi.wps.cn/v7"; - String wsId = (worksheetId != null && !worksheetId.trim().isEmpty() && !worksheetId.equals(fileId)) ? worksheetId : fileId; - String url = baseUrl + "/airsheet/" + fileId + "/worksheets/" + wsId + "/values"; - if (range != null && !range.trim().isEmpty()) { - url += "?range=" + java.net.URLEncoder.encode(range, "UTF-8"); - } - log.debug("尝试使用/values子路径 - url: {}", url); - return WPS365ApiUtil.httpRequest("GET", url, accessToken, null); - } catch (Exception e2) { - log.error("使用/values子路径也失败", e2); - throw new RuntimeException("读取AirSheet数据失败: " + e.getMessage(), e); + log.warn("AirSheet api/v1 路径失败,尝试 v7 - {}", e.getMessage()); + } + + // 方案2:兼容旧版 v7 路径(若官方恢复或环境不同) + try { + String url = "https://openapi.wps.cn/v7/airsheet/" + fileId + "/sheets/" + sheetIdx + "/cells"; + if (range != null && !range.trim().isEmpty()) { + url += "?range=" + java.net.URLEncoder.encode(range, "UTF-8"); } + return WPS365ApiUtil.httpRequest("GET", url, accessToken, null); + } catch (Exception e2) { + throw new RuntimeException("读取AirSheet数据失败: " + e2.getMessage(), e2); + } + } + + /** 解析工作表索引:0、空或与 fileId 相同时为 0,否则按数字解析 */ + private int parseSheetIndex(String worksheetId, String fileId) { + if (worksheetId == null || worksheetId.trim().isEmpty() || "0".equals(worksheetId) || (fileId != null && fileId.equals(worksheetId))) { + return 0; + } + try { + return Integer.parseInt(worksheetId.trim()); + } catch (NumberFormatException e) { + return 0; } } @Override public JSONObject updateAirSheetCells(String accessToken, String fileId, String worksheetId, String range, List> values) { - try { - // WPS365 AirSheet API路径格式 - // 根据文档:https://open.wps.cn/documents/app-integration-dev/wps365/server/airsheet/worksheets/VbHZwButmh - // 正确路径:https://openapi.wps.cn/v7/airsheet/{file_id}/worksheets - // 注意:路径中不需要 worksheet_id,只需要 file_id - String baseUrl = "https://openapi.wps.cn/v7"; - - // 使用官方文档中的正确路径格式 - String url = baseUrl + "/airsheet/" + fileId + "/worksheets"; - - // 构建请求体 - JSONObject requestBody = new JSONObject(); - if (range != null && !range.trim().isEmpty()) { - requestBody.put("range", range); - } - - // 构建values数组 - JSONArray valuesArray = new JSONArray(); - if (values != null) { - for (List row : values) { - JSONArray rowArray = new JSONArray(); - if (row != null) { - for (Object cell : row) { - rowArray.add(cell); - } + String baseUrl = wps365Config.getApiBaseUrl(); + int sheetIdx = parseSheetIndex(worksheetId, fileId); + + JSONObject requestBody = new JSONObject(); + if (range != null && !range.trim().isEmpty()) { + requestBody.put("range", range); + } + JSONArray valuesArray = new JSONArray(); + if (values != null) { + for (List row : values) { + JSONArray rowArray = new JSONArray(); + if (row != null) { + for (Object cell : row) { + rowArray.add(cell != null ? cell : ""); } - valuesArray.add(rowArray); } + valuesArray.add(rowArray); } - requestBody.put("values", valuesArray); - - // 如果指定了worksheetId,也可以添加到请求体中(如果API支持) - if (worksheetId != null && !worksheetId.trim().isEmpty() && !worksheetId.equals("0") && !worksheetId.equals(fileId)) { - requestBody.put("worksheet_id", worksheetId); - } - - String bodyStr = requestBody.toJSONString(); - log.debug("更新AirSheet数据 - url: {}, fileId: {}, worksheetId: {}, range: {}, values: {}", - url, fileId, worksheetId, range, bodyStr); - - return WPS365ApiUtil.httpRequest("PUT", url, accessToken, bodyStr); + } + requestBody.put("values", valuesArray); + String bodyStr = requestBody.toJSONString(); + + // 方案1:与 KSheet 一致的路径 POST /openapi/airsheet/{fileId}/sheets/{sheetIdx}/cells + try { + String url = baseUrl + "/openapi/airsheet/" + fileId + "/sheets/" + sheetIdx + "/cells"; + log.debug("更新AirSheet - url: {}, fileId: {}, sheetIdx: {}, range: {}", url, fileId, sheetIdx, range); + return WPS365ApiUtil.httpRequest("POST", url, accessToken, bodyStr); } catch (Exception e) { - log.error("更新AirSheet数据失败 - fileId: {}, worksheetId: {}, range: {}", fileId, worksheetId, range, e); - throw new RuntimeException("更新AirSheet数据失败: " + e.getMessage(), e); + log.warn("AirSheet api/v1 写入失败,尝试 v7 - {}", e.getMessage()); + } + + // 方案2:兼容 v7 PUT /airsheet/{file_id}/worksheets + try { + String url = "https://openapi.wps.cn/v7/airsheet/" + fileId + "/worksheets"; + return WPS365ApiUtil.httpRequest("PUT", url, accessToken, bodyStr); + } catch (Exception e2) { + throw new RuntimeException("更新AirSheet数据失败: " + e2.getMessage(), e2); } }