# 后端 WPS 回调接口需求文档 ## 问题分析 ### 当前错误 ``` init error: AppInfoNotExists won't start session GET https://o.wpsgo.com/api/v3/office/file/28/multiwatermark 403 (Forbidden) ``` ### 根本原因 WPS SDK 在初始化时会: 1. 向 WPS 服务器验证 appId 2. WPS 服务器会回调我们的后端接口获取文件信息 3. 如果回调失败,返回 AppInfoNotExists 错误 **结论**:必须实现后端回调接口,WPS SDK 才能正常工作。 ## 必需的后端接口 ### 1. 文件信息接口(最重要) **接口地址**: ``` GET /v1/3rd/file/info ``` **请求参数**(Query): - `_w_appid`: WPS 应用ID(SX20251229FLIAPDAPP) - `_w_fileid`: 文件ID(前端传递的 fileId) **请求头**: ``` X-WebOffice-Token: {前端传递的 token} X-User-Query: {前端传递的 customArgs,JSON 格式} ``` **响应格式**: ```json { "code": 0, "msg": "success", "data": { "file": { "id": "28", "name": "万颗星临床营养系统接口文档.pdf", "version": 1, "size": 1234567, "download_url": "http://img.tpidea.cn/2025/12/29/d94280895eac4a499da425a3564c69b7.pdf", "creator": { "id": "user_1", "name": "张三" }, "create_time": 1735459200, "modify_time": 1735459200, "user_acl": { "rename": 1, "history": 1, "copy": 1, "export": 1, "print": 1, "read": 1, "update": 1, "comment": 1 } } } } ``` **字段说明**: - `id`: 文件ID,必须与请求的 `_w_fileid` 一致 - `name`: 文件名 - `version`: 文件版本号,从 1 开始 - `size`: 文件大小(字节) - `download_url`: 文件下载地址,必须是可访问的 HTTP/HTTPS URL - `creator`: 创建者信息 - `create_time`: 创建时间(Unix 时间戳,秒) - `modify_time`: 修改时间(Unix 时间戳,秒) - `user_acl`: 用户权限配置 - `rename`: 是否允许重命名(1=允许,0=不允许) - `history`: 是否允许查看历史版本 - `copy`: 是否允许复制 - `export`: 是否允许导出 - `print`: 是否允许打印 - `read`: 是否允许查看(必须为 1) - `update`: 是否允许编辑(1=允许,0=只读) - `comment`: 是否允许批注 ### 2. WPS 应用配置 **在 WPS 开放平台控制台配置**: 1. 登录:https://open.wps.cn/ 2. 进入应用管理 3. 找到应用:SX20251229FLIAPDAPP 4. 配置回调地址: - 文件信息接口:`https://你的域名/v1/3rd/file/info` - 保存接口:`https://你的域名/v3/3rd/files` ### 3. 三阶段保存接口(可选,用于编辑保存) #### 阶段 1:准备上传 ``` GET /v3/3rd/files/:file_id/upload/prepare ``` **响应**: ```json { "code": 0, "data": { "digest_types": ["sha1", "md5"] } } ``` #### 阶段 2:获取上传地址 ``` POST /v3/3rd/files/:file_id/upload/address ``` **请求体**: ```json { "name": "文档.pdf", "size": 1234567, "digest": { "sha1": "abc123..." }, "is_manual": true } ``` **响应**: ```json { "code": 0, "data": { "method": "PUT", "url": "https://your-oss.com/upload/file123", "headers": {}, "send_back_params": {} } } ``` #### 阶段 3:上传完成通知 ``` POST /v3/3rd/files/:file_id/upload/complete ``` **请求体**: ```json { "request": { "name": "文档.pdf", "size": 1234567, "digest": { "sha1": "abc123..." }, "is_manual": true }, "response": { "status_code": 200, "headers": {} } } ``` **响应**: ```json { "code": 0, "data": { "id": "28", "name": "文档.pdf", "version": 2, "size": 1234567, "create_time": 1735459200, "modify_time": 1735459300, "creator_id": "user_1", "modifier_id": "user_1" } } ``` ## 实现优先级 ### P0(必须实现,否则无法使用) - ✅ 文件信息接口:`GET /v1/3rd/file/info` - ✅ WPS 控制台配置回调地址 ### P1(编辑功能需要) - 三阶段保存接口(如果只需要查看,可以暂不实现) ## 快速实现示例(Node.js/Express) ```javascript // 文件信息接口 app.get('/v1/3rd/file/info', async (req, res) => { const { _w_appid, _w_fileid } = req.query; // 验证 appId if (_w_appid !== 'SX20251229FLIAPDAPP') { return res.status(403).json({ code: 403, msg: 'Invalid appId' }); } // 从数据库获取文件信息 const document = await getDocumentById(_w_fileid); if (!document) { return res.status(404).json({ code: 404, msg: 'File not found' }); } // 返回文件信息 res.json({ code: 0, msg: 'success', data: { file: { id: document.id.toString(), name: document.fileName, version: document.version || 1, size: document.fileSize || 0, download_url: document.url, creator: { id: document.creatorId?.toString() || 'user_1', name: document.creatorName || '未知用户' }, create_time: Math.floor(new Date(document.createTime).getTime() / 1000), modify_time: Math.floor(new Date(document.updateTime).getTime() / 1000), user_acl: { rename: 1, history: 1, copy: 1, export: 1, print: 1, read: 1, update: 1, // 1=可编辑,0=只读 comment: 1 } } } }); }); ``` ## 快速实现示例(Java/Spring Boot) ```java @RestController @RequestMapping("/v1/3rd/file") public class WpsFileController { @Autowired private DocumentService documentService; @GetMapping("/info") public ResponseEntity getFileInfo( @RequestParam("_w_appid") String appId, @RequestParam("_w_fileid") String fileId ) { // 验证 appId if (!"SX20251229FLIAPDAPP".equals(appId)) { return ResponseEntity.status(403) .body(Map.of("code", 403, "msg", "Invalid appId")); } // 获取文件信息 Document document = documentService.getById(Long.parseLong(fileId)); if (document == null) { return ResponseEntity.status(404) .body(Map.of("code", 404, "msg", "File not found")); } // 构建响应 Map response = new HashMap<>(); response.put("code", 0); response.put("msg", "success"); Map fileInfo = new HashMap<>(); fileInfo.put("id", document.getId().toString()); fileInfo.put("name", document.getFileName()); fileInfo.put("version", document.getVersion() != null ? document.getVersion() : 1); fileInfo.put("size", document.getFileSize() != null ? document.getFileSize() : 0); fileInfo.put("download_url", document.getUrl()); Map creator = new HashMap<>(); creator.put("id", document.getCreatorId() != null ? document.getCreatorId().toString() : "user_1"); creator.put("name", document.getCreatorName() != null ? document.getCreatorName() : "未知用户"); fileInfo.put("creator", creator); fileInfo.put("create_time", document.getCreateTime().getTime() / 1000); fileInfo.put("modify_time", document.getUpdateTime().getTime() / 1000); Map userAcl = new HashMap<>(); userAcl.put("rename", 1); userAcl.put("history", 1); userAcl.put("copy", 1); userAcl.put("export", 1); userAcl.put("print", 1); userAcl.put("read", 1); userAcl.put("update", 1); // 1=可编辑,0=只读 userAcl.put("comment", 1); fileInfo.put("user_acl", userAcl); response.put("data", Map.of("file", fileInfo)); return ResponseEntity.ok(response); } } ``` ## 测试方法 ### 1. 使用 Postman 测试 ``` GET http://localhost:8080/v1/3rd/file/info?_w_appid=SX20251229FLIAPDAPP&_w_fileid=28 ``` ### 2. 使用 curl 测试 ```bash curl "http://localhost:8080/v1/3rd/file/info?_w_appid=SX20251229FLIAPDAPP&_w_fileid=28" ``` ### 3. 检查响应 确保响应格式完全符合上面的 JSON 格式。 ## 常见问题 ### Q1: 仍然报 AppInfoNotExists **原因**: 1. 后端接口未实现 2. 接口地址配置错误 3. 接口返回格式不正确 4. WPS 控制台未配置回调地址 **解决**: 1. 确认接口已实现并可访问 2. 检查 WPS 控制台的回调地址配置 3. 使用 Postman 测试接口返回格式 ### Q2: 403 Forbidden **原因**: 1. appId 验证失败 2. 文件不存在 3. 权限不足 **解决**: 1. 检查 appId 是否正确 2. 检查 fileId 对应的文件是否存在 3. 检查用户权限 ### Q3: 文件无法显示 **原因**: 1. download_url 无法访问 2. 文件格式不支持 3. 文件损坏 **解决**: 1. 确保 download_url 可以直接访问 2. 检查文件格式是否正确 3. 尝试直接下载文件验证 ## 下一步行动 ### 立即执行 1. 实现 `GET /v1/3rd/file/info` 接口 2. 使用 Postman 测试接口 3. 在 WPS 控制台配置回调地址 ### 验证步骤 1. 重启后端服务 2. 刷新前端页面 3. 打开审核对话框 4. 检查是否还有 AppInfoNotExists 错误 5. 验证文档是否正常显示 ## 参考资料 - [WPS 开放平台](https://open.wps.cn/) - [文件信息接口文档](https://solution.wps.cn/docs/callback/file.html) - [三阶段保存文档](https://solution.wps.cn/docs/callback/save.html)