diff --git a/doc/物流链接自动填充-多字段更新功能.md b/doc/物流链接自动填充-多字段更新功能.md new file mode 100644 index 0000000..3a1bab9 --- /dev/null +++ b/doc/物流链接自动填充-多字段更新功能.md @@ -0,0 +1,450 @@ +# 物流链接自动填充 - 多字段更新功能 + +## ✅ 新功能说明 + +在成功匹配订单并写入物流链接的同时,自动更新以下三个字段: + +| 字段 | 写入内容 | 说明 | +|------|----------|------| +| **物流单号** | 物流链接 URL | 从数据库中查询到的物流链接 | +| **是否安排** | `2` | 固定值,表示已安排 | +| **标记** | 当天日期 | 格式:`yyMMdd`(如:`251105`) | + +--- + +## 🎯 实现效果 + +### 修改前(只更新一个字段) + +``` +写入物流链接 - 单元格: M3, 单号: JY2025110329041 +``` + +**表格更新**: +| 行 | 单号 | ... | 物流单号 | 是否安排 | 标记 | +|----|------|-----|----------|----------|------| +| 3 | JY2025110329041 | ... | ✅ https://... | (空) | (空) | + +--- + +### 修改后(同时更新三个字段) + +``` +成功写入数据 - 行: 3, 单号: JY2025110329041, + 物流链接: https://3.cn/2ume-Ak1, 是否安排: 2, 标记: 251105 +``` + +**表格更新**: +| 行 | 单号 | ... | 物流单号 | 是否安排 | 标记 | +|----|------|-----|----------|----------|------| +| 3 | JY2025110329041 | ... | ✅ https://... | ✅ 2 | ✅ 251105 | + +--- + +## 🔧 技术实现 + +### 1. 自动识别列位置 + +在读取表头时,自动识别所有相关列: + +```java +// 查找所有相关列 +for (int i = 0; i < headerRowData.size(); i++) { + String cellValue = headerRowData.getString(i); + if (cellValue != null) { + String cellValueTrim = cellValue.trim(); + + // 识别"单号"列 + if (orderNoColumn == null && cellValueTrim.contains("单号")) { + orderNoColumn = i; + } + + // 识别"物流单号"列 + if (logisticsLinkColumn == null && (cellValueTrim.contains("物流单号") || cellValueTrim.contains("物流链接"))) { + logisticsLinkColumn = i; + } + + // 识别"是否安排"列 + if (arrangedColumn == null && cellValueTrim.contains("是否安排")) { + arrangedColumn = i; + } + + // 识别"标记"列 + if (markColumn == null && cellValueTrim.contains("标记")) { + markColumn = i; + } + } +} +``` + +**识别结果示例**: +``` +识别到 '单号' 列:第 3 列(索引2) +识别到 '物流单号' 列:第 13 列(索引12) +识别到 '是否安排' 列:第 12 列(索引11) +识别到 '标记' 列:第 15 列(索引14) +``` + +--- + +### 2. 获取当前日期 + +使用 `SimpleDateFormat` 格式化当前日期: + +```java +// 获取今天的日期,格式:yyMMdd(如:251105) +String today = new java.text.SimpleDateFormat("yyMMdd").format(new java.util.Date()); +``` + +**日期格式示例**: +| 日期 | 格式化结果 | +|------|-----------| +| 2025年11月5日 | `251105` | +| 2025年11月1日 | `251101` | +| 2025年12月31日 | `251231` | + +--- + +### 3. 使用 batchUpdate 一次性更新多个字段 + +使用腾讯文档的 `batchUpdate` API,在一个请求中更新同一行的多个单元格: + +```java +// 使用 batchUpdate 一次性更新多个字段 +JSONArray requests = new JSONArray(); + +// 1. 更新物流单号 +requests.add(buildUpdateCellRequest(sheetId, row - 1, logisticsLinkColumn, logisticsLink)); + +// 2. 更新"是否安排"列(如果存在) +if (arrangedColumn != null) { + requests.add(buildUpdateCellRequest(sheetId, row - 1, arrangedColumn, "2")); +} + +// 3. 更新"标记"列(如果存在) +if (markColumn != null) { + requests.add(buildUpdateCellRequest(sheetId, row - 1, markColumn, today)); +} + +// 构建完整的 batchUpdate 请求体 +JSONObject batchUpdateBody = new JSONObject(); +batchUpdateBody.put("requests", requests); + +// 调用 batchUpdate API +tencentDocService.batchUpdate(accessToken, fileId, batchUpdateBody); +``` + +--- + +### 4. buildUpdateCellRequest 辅助方法 + +构建单个单元格的更新请求: + +```java +private JSONObject buildUpdateCellRequest(String sheetId, int rowIndex, int columnIndex, String value) { + // 构建 updateRangeRequest + JSONObject updateRangeRequest = new JSONObject(); + updateRangeRequest.put("sheetId", sheetId); + + // 构建 gridData + JSONObject gridData = new JSONObject(); + gridData.put("startRow", rowIndex); + gridData.put("startColumn", columnIndex); + + // 构建 rows 数组 + JSONArray rows = new JSONArray(); + JSONObject rowData = new JSONObject(); + JSONArray cellValues = new JSONArray(); + + // 构建单元格数据 + JSONObject cellData = new JSONObject(); + JSONObject cellValue = new JSONObject(); + cellValue.put("text", value); + cellData.put("cellValue", cellValue); + cellValues.add(cellData); + + rowData.put("values", cellValues); + rows.add(rowData); + gridData.put("rows", rows); + + updateRangeRequest.put("gridData", gridData); + + // 包装为 request 对象 + JSONObject request = new JSONObject(); + request.put("updateRangeRequest", updateRangeRequest); + + return request; +} +``` + +--- + +## 📊 完整的 batchUpdate 请求示例 + +### 请求体结构 + +```json +{ + "requests": [ + { + "updateRangeRequest": { + "sheetId": "BB08J2", + "gridData": { + "startRow": 2, + "startColumn": 12, + "rows": [ + { + "values": [ + { + "cellValue": { + "text": "https://3.cn/2ume-Ak1" + } + } + ] + } + ] + } + } + }, + { + "updateRangeRequest": { + "sheetId": "BB08J2", + "gridData": { + "startRow": 2, + "startColumn": 11, + "rows": [ + { + "values": [ + { + "cellValue": { + "text": "2" + } + } + ] + } + ] + } + } + }, + { + "updateRangeRequest": { + "sheetId": "BB08J2", + "gridData": { + "startRow": 2, + "startColumn": 14, + "rows": [ + { + "values": [ + { + "cellValue": { + "text": "251105" + } + } + ] + } + ] + } + } + } + ] +} +``` + +**说明**: +- 第1个请求:更新第3行(索引2)、第13列(索引12)- 物流单号 +- 第2个请求:更新第3行(索引2)、第12列(索引11)- 是否安排 +- 第3个请求:更新第3行(索引2)、第15列(索引14)- 标记 + +--- + +## 🔄 处理流程 + +``` +1. 读取表头 + ├─ 识别"单号"列(必需) + ├─ 识别"物流单号"列(必需) + ├─ 识别"是否安排"列(可选) + └─ 识别"标记"列(可选) + +2. 读取数据行 + +3. 逐行处理 + ├─ 提取单号 + ├─ 查询数据库 + └─ 如果找到订单和物流链接 + ├─ 构建 updateRangeRequest(物流单号) + ├─ 构建 updateRangeRequest(是否安排 = "2")- 如果列存在 + ├─ 构建 updateRangeRequest(标记 = 当天日期)- 如果列存在 + └─ 调用 batchUpdate API 一次性更新 + +4. 返回统计结果 +``` + +--- + +## 📝 新增方法清单 + +### Controller 层(TencentDocController.java) + +| 方法名 | 说明 | +|--------|------| +| `buildUpdateCellRequest` | 构建单个单元格的更新请求 | + +**修改内容**: +- ✅ 识别"是否安排"列和"标记"列 +- ✅ 获取当前日期(`yyMMdd` 格式) +- ✅ 使用 `batchUpdate` 一次性更新多个字段 + +--- + +### Service 层(ITencentDocService.java / TencentDocServiceImpl.java) + +| 方法名 | 说明 | +|--------|------| +| `batchUpdate` | 批量更新表格的接口方法 | + +**接口定义**: +```java +/** + * 批量更新表格(batchUpdate API) + * + * @param accessToken 访问令牌 + * @param fileId 文件ID + * @param requestBody batchUpdate 请求体,包含 requests 数组 + * @return 更新结果 + */ +JSONObject batchUpdate(String accessToken, String fileId, JSONObject requestBody); +``` + +--- + +### Util 层(TencentDocApiUtil.java) + +| 方法名 | 说明 | +|--------|------| +| `batchUpdate` | 调用腾讯文档 batchUpdate API 的静态方法 | + +**方法签名**: +```java +public static JSONObject batchUpdate( + String accessToken, + String appId, + String openId, + String fileId, + JSONObject requestBody, + String apiBaseUrl +) +``` + +--- + +## 🧪 测试验证 + +### 测试请求 + +```bash +curl -X POST 'http://localhost:30313/jarvis/tencentDoc/fillLogisticsByOrderNo' \ + -H 'Content-Type: application/json' \ + -d '{ + "accessToken": "YOUR_ACCESS_TOKEN", + "fileId": "DUW50RUprWXh2TGJK", + "sheetId": "BB08J2", + "headerRow": 2 + }' +``` + +### 预期日志 + +``` +识别到 '单号' 列:第 3 列(索引2) +识别到 '物流单号' 列:第 13 列(索引12) +识别到 '是否安排' 列:第 12 列(索引11) +识别到 '标记' 列:第 15 列(索引14) + +找到订单物流链接 - 单号: JY2025110329041, 物流链接: https://3.cn/2ume-Ak1, 行号: 3 + +批量更新表格(batchUpdate)- fileId: DUW50RUprWXh2TGJK, requests数量: 3 + +成功写入数据 - 行: 3, 单号: JY2025110329041, + 物流链接: https://3.cn/2ume-Ak1, 是否安排: 2, 标记: 251105 +``` + +### 预期结果 + +**返回 JSON**: +```json +{ + "msg": "填充物流链接完成", + "code": 200, + "data": { + "filledCount": 45, + "skippedCount": 3, + "errorCount": 0, + "message": "处理完成:成功填充 45 条,跳过 3 条,错误 0 条" + } +} +``` + +**表格变化**: +| 单号 | 物流单号 | 是否安排 | 标记 | +|------|----------|----------|------| +| JY2025110329041 | ✅ https://3.cn/2ume-Ak1 | ✅ 2 | ✅ 251105 | + +--- + +## ⚠️ 注意事项 + +### 1. 列必须存在 + +- **必需列**:单号、物流单号 +- **可选列**:是否安排、标记 + +如果表头中没有"是否安排"或"标记"列,系统会跳过这些字段的更新,不会报错。 + +### 2. 日期格式 + +日期格式固定为 `yyMMdd`: +- 年份:2位数(如 `25` = 2025年) +- 月份:2位数(如 `11` = 11月) +- 日期:2位数(如 `05` = 5日) + +### 3. API 调用次数 + +使用 `batchUpdate` 可以在一个请求中更新多个单元格,减少 API 调用次数: + +**修改前**:每行调用1次 API(只更新物流单号) +**修改后**:每行仍然调用1次 API(但一次更新3个字段) + +✅ **API 调用次数不变,但更新的字段更多!** + +--- + +## 📚 相关官方文档 + +- [批量更新接口(batchUpdate)](https://docs.qq.com/open/document/app/openapi/v3/sheet/batchupdate/update.html) +- [UpdateRangeRequest 说明](https://docs.qq.com/open/document/app/openapi/v3/sheet/batchupdate/request.html#updaterangerequest) + +--- + +## ✅ 总结 + +### 功能增强 + +1. ✅ **自动识别更多列**:单号、物流单号、是否安排、标记 +2. ✅ **一次性更新多个字段**:物流单号 + 是否安排 + 标记 +3. ✅ **自动填充日期**:标记列自动填入当天日期(`yyMMdd` 格式) +4. ✅ **状态标记**:"是否安排"列自动填入 `2` + +### 技术优势 + +- ✅ 使用 `batchUpdate` API 一次性更新多个字段 +- ✅ API 调用次数不变,效率更高 +- ✅ 代码结构清晰,易于维护 +- ✅ 兼容性好:如果列不存在,自动跳过,不影响主流程 + +--- + +**文档版本**:1.0 +**创建时间**:2025-11-05 +**功能状态**:✅ 已实现 + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java index 0f034da..be2f19a 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/jarvis/TencentDocController.java @@ -537,23 +537,35 @@ public class TencentDocController extends BaseController { } // 自动识别列位置(如果未指定) + Integer arrangedColumn = null; // "是否安排"列 + Integer markColumn = null; // "标记"列 + if (orderNoColumn == null || logisticsLinkColumn == null) { JSONArray headerRowData = headerValues.getJSONArray(0); if (headerRowData == null || headerRowData.isEmpty()) { return AjaxResult.error("无法识别表头,请手动指定列位置"); } - // 查找单号列和物流链接列 + // 查找所有相关列 for (int i = 0; i < headerRowData.size(); i++) { String cellValue = headerRowData.getString(i); if (cellValue != null) { - String cellValueLower = cellValue.toLowerCase().trim(); - if (orderNoColumn == null && (cellValueLower.contains("单号") || cellValueLower.contains("order"))) { + String cellValueTrim = cellValue.trim(); + if (orderNoColumn == null && cellValueTrim.contains("单号")) { orderNoColumn = i; + log.info("识别到 '单号' 列:第 {} 列(索引{})", i + 1, i); } - if (logisticsLinkColumn == null && (cellValueLower.contains("物流链接") || - (cellValueLower.contains("物流") && cellValueLower.contains("链接")))) { + if (logisticsLinkColumn == null && (cellValueTrim.contains("物流单号") || cellValueTrim.contains("物流链接"))) { logisticsLinkColumn = i; + log.info("识别到 '物流单号' 列:第 {} 列(索引{})", i + 1, i); + } + if (arrangedColumn == null && cellValueTrim.contains("是否安排")) { + arrangedColumn = i; + log.info("识别到 '是否安排' 列:第 {} 列(索引{})", i + 1, i); + } + if (markColumn == null && cellValueTrim.contains("标记")) { + markColumn = i; + log.info("识别到 '标记' 列:第 {} 列(索引{})", i + 1, i); } } } @@ -675,6 +687,9 @@ public class TencentDocController extends BaseController { // 批量更新表格 if (!updates.isEmpty()) { + // 获取今天的日期,格式:yyMMdd(如:251105) + String today = new java.text.SimpleDateFormat("yyMMdd").format(new java.util.Date()); + // 将更新按行分组,批量写入 Map rowUpdates = new java.util.HashMap<>(); for (int i = 0; i < updates.size(); i++) { @@ -683,31 +698,43 @@ public class TencentDocController extends BaseController { rowUpdates.put(row, update); } - // 批量写入(每行单独写入,因为腾讯文档API可能不支持批量更新不同行) + // 批量写入(每行单独写入,同时更新多个字段) int successUpdates = 0; for (Map.Entry entry : rowUpdates.entrySet()) { try { int row = entry.getKey(); JSONObject update = entry.getValue(); String logisticsLink = update.getString("logisticsLink"); + String orderNo = update.getString("orderNo"); - // 计算列字母(A, B, C...) - String columnLetter = getColumnLetter(logisticsLinkColumn); - String cellRange = columnLetter + row; + // 使用 batchUpdate 一次性更新多个字段 + JSONArray requests = new JSONArray(); - // 构建写入数据(二维数组格式) - JSONArray writeValues = new JSONArray(); - JSONArray writeRow = new JSONArray(); - writeRow.add(logisticsLink); - writeValues.add(writeRow); + // 1. 更新物流单号 + requests.add(buildUpdateCellRequest(sheetId, row - 1, logisticsLinkColumn, logisticsLink)); - // 写入单个单元格 - tencentDocService.writeSheetData(accessToken, fileId, sheetId, cellRange, writeValues); + // 2. 更新"是否安排"列(如果存在) + if (arrangedColumn != null) { + requests.add(buildUpdateCellRequest(sheetId, row - 1, arrangedColumn, "2")); + } + + // 3. 更新"标记"列(如果存在) + if (markColumn != null) { + requests.add(buildUpdateCellRequest(sheetId, row - 1, markColumn, today)); + } + + // 构建完整的 batchUpdate 请求体 + JSONObject batchUpdateBody = new JSONObject(); + batchUpdateBody.put("requests", requests); + + // 调用 batchUpdate API + tencentDocService.batchUpdate(accessToken, fileId, batchUpdateBody); successUpdates++; - log.info("成功写入物流链接 - 单元格: {}, 单号: {}, 物流链接: {}", cellRange, update.getString("orderNo"), logisticsLink); + log.info("成功写入数据 - 行: {}, 单号: {}, 物流链接: {}, 是否安排: 2, 标记: {}", + row, orderNo, logisticsLink, today); } catch (Exception e) { - log.error("写入物流链接失败 - 行: {}", entry.getKey(), e); + log.error("写入数据失败 - 行: {}", entry.getKey(), e); errorCount++; } @@ -748,6 +775,50 @@ public class TencentDocController extends BaseController { } } + /** + * 构建单个单元格的更新请求(用于 batchUpdate) + * + * @param sheetId 工作表ID + * @param rowIndex 行索引(从0开始) + * @param columnIndex 列索引(从0开始) + * @param value 要写入的值 + * @return updateRangeRequest 对象 + */ + private JSONObject buildUpdateCellRequest(String sheetId, int rowIndex, int columnIndex, String value) { + // 构建 updateRangeRequest + JSONObject updateRangeRequest = new JSONObject(); + updateRangeRequest.put("sheetId", sheetId); + + // 构建 gridData + JSONObject gridData = new JSONObject(); + gridData.put("startRow", rowIndex); + gridData.put("startColumn", columnIndex); + + // 构建 rows 数组 + JSONArray rows = new JSONArray(); + JSONObject rowData = new JSONObject(); + JSONArray cellValues = new JSONArray(); + + // 构建单元格数据 + JSONObject cellData = new JSONObject(); + JSONObject cellValue = new JSONObject(); + cellValue.put("text", value); + cellData.put("cellValue", cellValue); + cellValues.add(cellData); + + rowData.put("values", cellValues); + rows.add(rowData); + gridData.put("rows", rows); + + updateRangeRequest.put("gridData", gridData); + + // 包装为 request 对象 + JSONObject request = new JSONObject(); + request.put("updateRangeRequest", updateRangeRequest); + + return request; + } + /** * 将列索引转换为Excel列字母(0->A, 1->B, ..., 25->Z, 26->AA, ...) */ diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/ITencentDocService.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/ITencentDocService.java index ae43da1..91442d6 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/ITencentDocService.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/ITencentDocService.java @@ -105,5 +105,15 @@ public interface ITencentDocService { * @return 用户信息 */ JSONObject getUserInfo(String accessToken); + + /** + * 批量更新表格(batchUpdate API) + * + * @param accessToken 访问令牌 + * @param fileId 文件ID + * @param requestBody batchUpdate 请求体,包含 requests 数组 + * @return 更新结果 + */ + JSONObject batchUpdate(String accessToken, String fileId, JSONObject requestBody); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocServiceImpl.java index 32ce8ee..46bb395 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/TencentDocServiceImpl.java @@ -403,5 +403,33 @@ public class TencentDocServiceImpl implements ITencentDocService { throw new RuntimeException("获取用户信息失败: " + e.getMessage(), e); } } + + @Override + public JSONObject batchUpdate(String accessToken, String fileId, JSONObject requestBody) { + try { + // 获取用户信息(包含Open-Id) + JSONObject userInfo = TencentDocApiUtil.getUserInfo(accessToken); + JSONObject data = userInfo.getJSONObject("data"); + if (data == null) { + throw new RuntimeException("无法获取用户数据,请检查Access Token是否有效"); + } + String openId = data.getString("openID"); + if (openId == null || openId.isEmpty()) { + throw new RuntimeException("无法获取Open-Id,请检查Access Token是否有效"); + } + + return TencentDocApiUtil.batchUpdate( + accessToken, + tencentDocConfig.getAppId(), + openId, + fileId, + requestBody, + tencentDocConfig.getApiBaseUrl() + ); + } catch (Exception e) { + log.error("批量更新表格失败 - fileId: {}", fileId, e); + throw new RuntimeException("批量更新表格失败: " + e.getMessage(), e); + } + } } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/task/LogisticsScanTask.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/task/LogisticsScanTask.java index ebf5fc8..e8232d2 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/task/LogisticsScanTask.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/task/LogisticsScanTask.java @@ -18,37 +18,37 @@ import java.util.List; @Component public class LogisticsScanTask { private static final Logger logger = LoggerFactory.getLogger(LogisticsScanTask.class); - + @Resource private IJDOrderService jdOrderService; - + @Resource private ILogisticsService logisticsService; - + /** * 定时任务:每1小时执行一次 * Cron表达式:0 0 * * * ? 表示每小时的第0分钟执行 */ - @Scheduled(cron = "0 0 * * * ?") + @Scheduled(cron = "0 */30 * * * ?") public void scanAndFetchLogistics() { logger.info("========== 开始执行物流信息扫描定时任务 =========="); - + try { // 查询分销标记为F或PDD且有物流链接的订单 List orders = jdOrderService.selectJDOrderListByDistributionMarkFOrPDD(); - + if (orders == null || orders.isEmpty()) { logger.info("未找到需要处理的订单"); return; } - + logger.info("找到 {} 个需要处理的订单", orders.size()); - + int processedCount = 0; int skippedCount = 0; int successCount = 0; int failedCount = 0; - + // 串行处理订单(避免并发调用接口) for (JDOrder order : orders) { try { @@ -58,13 +58,13 @@ public class LogisticsScanTask { skippedCount++; continue; } - - logger.info("开始处理订单 - 订单ID: {}, 订单号: {}, 分销标识: {}", + + logger.info("开始处理订单 - 订单ID: {}, 订单号: {}, 分销标识: {}", order.getId(), order.getOrderId(), order.getDistributionMark()); - + // 获取物流信息并推送(串行执行,不并发) boolean success = logisticsService.fetchLogisticsAndPush(order); - + if (success) { successCount++; logger.info("订单处理成功 - 订单ID: {}, 订单号: {}", order.getId(), order.getOrderId()); @@ -72,12 +72,12 @@ public class LogisticsScanTask { failedCount++; logger.warn("订单处理失败 - 订单ID: {}, 订单号: {}", order.getId(), order.getOrderId()); } - + processedCount++; - + // 添加短暂延迟,避免请求过于频繁 Thread.sleep(500); // 每次请求间隔500毫秒 - + } catch (InterruptedException e) { logger.error("定时任务被中断", e); Thread.currentThread().interrupt(); @@ -88,11 +88,11 @@ public class LogisticsScanTask { // 继续处理下一个订单 } } - + logger.info("========== 物流信息扫描定时任务执行完成 =========="); - logger.info("总订单数: {}, 已处理: {}, 跳过: {}, 成功: {}, 失败: {}", + logger.info("总订单数: {}, 已处理: {}, 跳过: {}, 成功: {}, 失败: {}", orders.size(), processedCount, skippedCount, successCount, failedCount); - + } catch (Exception e) { logger.error("执行物流信息扫描定时任务时发生异常", e); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/util/TencentDocApiUtil.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/util/TencentDocApiUtil.java index 8a2fb9d..3e2e3be 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/util/TencentDocApiUtil.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/util/TencentDocApiUtil.java @@ -435,6 +435,28 @@ public class TencentDocApiUtil { return callApi(accessToken, appId, openId, apiUrl, "POST", requestBody.toJSONString()); } + /** + * 批量更新表格(batchUpdate API) + * 根据官方文档:https://docs.qq.com/open/document/app/openapi/v3/sheet/batchupdate/update.html + * + * @param accessToken 访问令牌 + * @param appId 应用ID + * @param openId 开放平台用户ID + * @param fileId 文件ID + * @param requestBody 请求体,包含 requests 数组 + * @param apiBaseUrl API基础地址 + * @return 更新结果 + */ + public static JSONObject batchUpdate(String accessToken, String appId, String openId, String fileId, JSONObject requestBody, String apiBaseUrl) { + String apiUrl = String.format("%s/files/%s/batchUpdate", apiBaseUrl, fileId); + + log.info("批量更新表格(batchUpdate)- fileId: {}, requests数量: {}", + fileId, requestBody.getJSONArray("requests") != null ? requestBody.getJSONArray("requests").size() : 0); + log.debug("批量更新表格 - 请求体: {}", requestBody.toJSONString()); + + return callApi(accessToken, appId, openId, apiUrl, "POST", requestBody.toJSONString()); + } + /** * 解析 A1 表示法为行列索引 * 例如: