1
This commit is contained in:
39
doc/WPS365智能表格AirSheet接口说明.md
Normal file
39
doc/WPS365智能表格AirSheet接口说明.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# WPS365 智能表格(AirSheet)接口说明
|
||||
|
||||
## 当前情况
|
||||
|
||||
- **智能表格(AirSheet)** 在 open.wps.cn 开放平台申请的权限为 `kso.airsheet.readwrite`,但 **openapi.wps.cn 下目前没有可用的 AirSheet REST 接口**:
|
||||
- `https://openapi.wps.cn/api/v1/openapi/airsheet/{fileId}/...` → 404
|
||||
- `https://openapi.wps.cn/v7/airsheet/{fileId}/...` → 404
|
||||
|
||||
因此直接按「智能表格」调 AirSheet 读/写会报 **读取AirSheet数据失败、statusCode=404**。
|
||||
|
||||
## 代码中的处理
|
||||
|
||||
- **读**:`readAirSheetCells` 会依次尝试
|
||||
1)AirSheet 路径 → 2)**KSheet(在线表格)路径**(同一 `fileId` 当作 `file_token`)→ 3)v7 AirSheet。
|
||||
若都 404,会抛出带说明的异常。
|
||||
- **写**:`updateAirSheetCells` 同样会先试 AirSheet,再回退到 **KSheet 写入**,再试 v7。
|
||||
|
||||
即:当 AirSheet 接口 404 时,会自动用同一 ID 试 **在线表格(KSheet)** 接口;若文档在 WPS 文件列表里是以 KSheet 形式存在的,有可能用 KSheet 读/写成功。
|
||||
|
||||
## 建议用法(避免 404)
|
||||
|
||||
1. **优先用「文件列表」返回的 file_token 调 KSheet 接口**
|
||||
- 调用 `GET /jarvis/wps365/files`(或等价的文件列表接口),拿到目标文档的 **file_token**。
|
||||
- 用该 **file_token** 调用 **KSheet** 读/写接口,不要用浏览器地址栏或分享链接里的 ID 当作 file_token:
|
||||
- 读:`GET /jarvis/wps365/readCells?userId=xxx&fileToken=文件列表返回的file_token&sheetIdx=0&range=A1:B5`
|
||||
- 写:`POST /jarvis/wps365/updateCells`,body 里 `fileToken` 填文件列表返回的 file_token。
|
||||
- 这样即使用户创建的是「智能表格」,只要在文件列表里存在且后端用 KSheet 能访问,就可正常读/写。
|
||||
|
||||
2. **确认文档来源**
|
||||
- 若文档是在 **金山文档(kdocs.cn)** 创建的,当前项目用的是 **open.wps.cn(WPS365)** 的 OAuth 与 API,token 不能直接访问金山文档。
|
||||
- 要在后端读/写金山文档里的表格,需要接 **金山文档开放平台(developer.kdocs.cn)** 的 KSheet 服务端 API,并使用该平台的鉴权方式。
|
||||
|
||||
3. **若必须用「智能表格」且仅支持 AirScript**
|
||||
- 若官方仅提供通过 **AirScript(脚本)** 操作智能表格(例如 `POST https://www.kdocs.cn/api/v3/ide/file/:file_id/script/:script_id/sync_task`),则需在表格内编写脚本,后端只调该脚本接口,而不是直接调 REST 读单元格。
|
||||
|
||||
## 小结
|
||||
|
||||
- **404 原因**:openapi.wps.cn 下当前没有可用的智能表格(AirSheet)REST 读/写接口。
|
||||
- **可行方案**:用 **文件列表接口拿 file_token**,用 **KSheet 读/写接口**(readCells / updateCells)操作该文档;若文档在金山文档,需改用金山文档开放平台的 API。
|
||||
65
doc/WPS365获取文件列表-测试说明.md
Normal file
65
doc/WPS365获取文件列表-测试说明.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# WPS365 获取文件列表 - 测试说明
|
||||
|
||||
## 接口说明
|
||||
|
||||
- **后端接口**:`GET /jarvis/wps365/files`
|
||||
- **参数**:
|
||||
- `userId`(必填):与授权时保存 Token 使用的用户标识,前端 WPS365 页目前固定为 `default_user`
|
||||
- `page`(可选,默认 1):页码
|
||||
- `pageSize`(可选,默认 20):每页条数
|
||||
- **鉴权**:需登录系统(请求头带 JWT),后端用 `userId` 从 Redis 取 WPS365 的 access_token,再请求 WPS 云文档文件列表 API。
|
||||
|
||||
## 前置条件
|
||||
|
||||
1. **已完成 WPS365 授权**
|
||||
在系统里至少完成一次 WPS365 授权,且回调成功后 Token 已保存(与 `userId` 对应,一般为 `default_user`)。
|
||||
2. **已登录系统**
|
||||
使用已登录账号访问前端或调用接口(需携带有效 JWT)。
|
||||
|
||||
## 测试方式一:前端页面(推荐)
|
||||
|
||||
1. 登录若依前端。
|
||||
2. 打开 **WPS365 在线表格管理** 页面:侧边栏 **文档同步配置** → **WPS365 在线表格管理**(或直接访问 `/docSync/wps365`)。
|
||||
3. 若未授权:点击「立即授权」,在新窗口完成 WPS365 授权后关闭,回到本页。
|
||||
4. 确认顶部为「已授权」绿色提示。
|
||||
5. 在「文件列表」卡片中点击 **「加载文件」**。
|
||||
6. 观察:
|
||||
- 表格中是否出现文件行(文件名、文件 Token、类型等);
|
||||
- 浏览器开发者工具 → 网络:找到 `files` 请求,查看请求 URL 是否为 `/jarvis/wps365/files?userId=default_user&page=1&pageSize=20`,以及响应 body 中是否有文件数据。
|
||||
|
||||
**预期**:能拉取到 WPS 云文档中的文件列表;列表中每条会包含 `file_token`,后续读/写单元格需使用该 `file_token`。
|
||||
|
||||
若提示「用户未授权」:说明当前 `userId`(如 `default_user`)下没有 WPS365 Token,需重新走一遍授权并确保回调成功。
|
||||
若提示「获取文件列表失败」且带 HTTP 状态码或 WPS 错误信息:需看后端日志中请求的 WPS API 地址与返回内容,再对照 [WPS 开放平台-云文档](https://open.wps.cn/documents/app-integration-dev/wps365/server/yundoc/introduce.html) 文档排查。
|
||||
|
||||
## 测试方式二:curl(需 JWT)
|
||||
|
||||
1. 从浏览器登录后,在开发者工具中复制当前请求的 `Authorization` 头(或从 localStorage 等获取 token 拼成 `Bearer <token>`)。
|
||||
2. 执行(将 `YOUR_JWT` 和 `BASE_URL` 替换为实际值):
|
||||
|
||||
```bash
|
||||
curl -s -X GET "http://localhost:30313/jarvis/wps365/files?userId=default_user&page=1&pageSize=20" \
|
||||
-H "Authorization: Bearer YOUR_JWT"
|
||||
```
|
||||
|
||||
3. 查看响应:
|
||||
- `code: 200` 且 `data` 中有文件列表(如 `data.files`、`data.total` 等)则说明获取文件列表能力正常;
|
||||
- `msg` 为「用户未授权」则需先完成 WPS365 授权;
|
||||
- 其他错误可根据 `msg` 或后端日志排查。
|
||||
|
||||
## 后端实际请求的 WPS API
|
||||
|
||||
- **URL**:`{apiBaseUrl}/yundoc/files`,例如 `https://openapi.wps.cn/api/v1/yundoc/files`
|
||||
- **方法**:GET
|
||||
- **Query**:`page`、`page_size`(由后端把 `page` 和 `pageSize` 映射过去)
|
||||
- **鉴权**:Header 中带 WPS365 的 `access_token`
|
||||
|
||||
若 WPS 侧返回 404 或参数错误,需对照 [WPS 开放平台-云文档 API](https://open.wps.cn/documents/app-integration-dev/wps365/server/yundoc/introduce.html) 确认路径、参数名(如是否需 `drive_id`、`parent_id` 等)是否与当前实现一致,必要时调整 `WPS365ApiServiceImpl.getFileList` 中的 URL 与参数。
|
||||
|
||||
## 成功后的下一步
|
||||
|
||||
- 从文件列表结果中取目标文件的 **`file_token`**。
|
||||
- 读单元格:使用 **KSheet 读接口** `readCells`,参数中 `fileToken` 填该 `file_token`。
|
||||
- 写单元格:使用 **KSheet 写接口** `updateCells`,同样使用该 `file_token`。
|
||||
|
||||
这样可避免用分享链接里的 ID 直接调接口导致的 404,改为用「文件列表」返回的 `file_token` 访问表格。
|
||||
@@ -219,31 +219,45 @@ 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 路径风格一致)
|
||||
// 智能表格(AirSheet):openapi.wps.cn 下 airsheet 接口目前均返回 404,先试 AirSheet 再回退到 KSheet(同一 fileId 作为 file_token 试读)
|
||||
String baseUrl = wps365Config.getApiBaseUrl();
|
||||
int sheetIdx = parseSheetIndex(worksheetId, fileId);
|
||||
|
||||
// 方案1:与 KSheet 一致的路径 GET /openapi/airsheet/{fileId}/sheets/{sheetIdx}/cells?range=...
|
||||
// 方案1:AirSheet 路径 GET /openapi/airsheet/{fileId}/sheets/{sheetIdx}/cells
|
||||
try {
|
||||
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("读取AirSheet - url: {}, fileId: {}, sheetIdx: {}, range: {}", url, fileId, sheetIdx, range);
|
||||
log.debug("读取AirSheet - url: {}, fileId: {}, sheetIdx: {}", url, fileId, sheetIdx);
|
||||
return WPS365ApiUtil.httpRequest("GET", url, accessToken, null);
|
||||
} catch (Exception e) {
|
||||
log.warn("AirSheet api/v1 路径失败,尝试 v7 - {}", e.getMessage());
|
||||
log.warn("AirSheet 接口 404 或失败,尝试 KSheet 回退 - {}", e.getMessage());
|
||||
}
|
||||
|
||||
// 方案2:兼容旧版 v7 路径(若官方恢复或环境不同)
|
||||
// 方案2:用同一 ID 走 KSheet(在线表格)接口——若文档在「文件列表」里以 KSheet 形式存在则可用
|
||||
try {
|
||||
String url = baseUrl + "/openapi/ksheet/" + fileId + "/sheets/" + sheetIdx + "/cells";
|
||||
if (range != null && !range.trim().isEmpty()) {
|
||||
url += "?range=" + java.net.URLEncoder.encode(range, "UTF-8");
|
||||
}
|
||||
log.debug("回退到 KSheet 读取 - url: {}, fileId 作 file_token: {}", url, fileId);
|
||||
return WPS365ApiUtil.httpRequest("GET", url, accessToken, null);
|
||||
} catch (Exception e2) {
|
||||
log.warn("KSheet 回退也失败 - {}", e2.getMessage());
|
||||
}
|
||||
|
||||
// 方案3:兼容旧版 v7 airsheet(若官方恢复)
|
||||
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);
|
||||
} catch (Exception e3) {
|
||||
throw new RuntimeException(
|
||||
"读取智能表格失败:AirSheet 与 KSheet 接口均不可用(404)。请确认:1) 使用「文件列表」接口返回的 file_token 再试;2) 若文档在金山文档(kdocs.cn)创建,需用金山文档开放平台(developer.kdocs.cn)的 KSheet API。原始错误: " + e3.getMessage(),
|
||||
e3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,21 +297,32 @@ public class WPS365ApiServiceImpl implements IWPS365ApiService {
|
||||
requestBody.put("values", valuesArray);
|
||||
String bodyStr = requestBody.toJSONString();
|
||||
|
||||
// 方案1:与 KSheet 一致的路径 POST /openapi/airsheet/{fileId}/sheets/{sheetIdx}/cells
|
||||
// 方案1:AirSheet 路径 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);
|
||||
log.debug("更新AirSheet - url: {}, fileId: {}, sheetIdx: {}", url, fileId, sheetIdx);
|
||||
return WPS365ApiUtil.httpRequest("POST", url, accessToken, bodyStr);
|
||||
} catch (Exception e) {
|
||||
log.warn("AirSheet api/v1 写入失败,尝试 v7 - {}", e.getMessage());
|
||||
log.warn("AirSheet 写入失败,尝试 KSheet 回退 - {}", e.getMessage());
|
||||
}
|
||||
|
||||
// 方案2:兼容 v7 PUT /airsheet/{file_id}/worksheets
|
||||
// 方案2:用同一 ID 走 KSheet 写入接口
|
||||
try {
|
||||
String url = baseUrl + "/openapi/ksheet/" + fileId + "/sheets/" + sheetIdx + "/cells";
|
||||
log.debug("回退到 KSheet 写入 - url: {}", url);
|
||||
return WPS365ApiUtil.httpRequest("POST", url, accessToken, bodyStr);
|
||||
} catch (Exception e2) {
|
||||
log.warn("KSheet 回退写入也失败 - {}", e2.getMessage());
|
||||
}
|
||||
|
||||
// 方案3:兼容 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);
|
||||
} catch (Exception e3) {
|
||||
throw new RuntimeException(
|
||||
"更新智能表格失败:AirSheet 与 KSheet 接口均不可用。请使用「文件列表」返回的 file_token 调用 KSheet 写入接口,或确认文档类型。原始错误: " + e3.getMessage(),
|
||||
e3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user