From 9500a1df1298fef2bd6c7c8afb39cb48823de0be Mon Sep 17 00:00:00 2001 From: liushuang Date: Wed, 15 Oct 2025 14:21:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(feishu):=20=E6=B7=BB=E5=8A=A0=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E5=AE=98=E6=96=B9Client=E8=BF=9E=E6=8E=A5=E6=B1=A0?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在FeishuClient中新增closeOfficialPool配置项,用于控制是否关闭官方SDK的连接池- 新增Builder模式下的closeOfficialPool方法,支持链式调用设置该选项 - 修改FsApiUtil工具类,在调用飞书API时根据配置决定是否添加Connection: close头部 - 更新FsClient初始化逻辑,支持传入closeOfficialPool参数并传递给FeishuClient - 调整部分日志级别,将info级别改为debug以减少生产环境日志量 -优化代码格式,移除不必要的空行和注释中的多余换行符号 --- .../cn/isliu/core/client/FeishuClient.java | 28 +++++++- .../java/cn/isliu/core/client/FsClient.java | 7 +- .../java/cn/isliu/core/utils/FsApiUtil.java | 67 +++++++++++-------- 3 files changed, 73 insertions(+), 29 deletions(-) diff --git a/src/main/java/cn/isliu/core/client/FeishuClient.java b/src/main/java/cn/isliu/core/client/FeishuClient.java index e442db5..cce848e 100644 --- a/src/main/java/cn/isliu/core/client/FeishuClient.java +++ b/src/main/java/cn/isliu/core/client/FeishuClient.java @@ -22,6 +22,7 @@ public class FeishuClient { private final OkHttpClient httpClient; private final String appId; private final String appSecret; + private final boolean closeOfficialPool; // 自定义服务,处理官方SDK未覆盖的API private volatile CustomSheetService customSheetService; @@ -37,8 +38,18 @@ public class FeishuClient { this.appSecret = appSecret; this.officialClient = officialClient; this.httpClient = httpClient; + this.closeOfficialPool = false; } + private FeishuClient(String appId, String appSecret, Client officialClient, OkHttpClient httpClient, boolean closeOfficialPool) { + this.appId = appId; + this.appSecret = appSecret; + this.officialClient = officialClient; + this.httpClient = httpClient; + this.closeOfficialPool = closeOfficialPool; + } + + /** * 创建客户端构建器 * @@ -216,12 +227,22 @@ public class FeishuClient { return appSecret; } + /** + * 获取是否关闭官方Client连接池 + * + * @return 是否关闭官方Client连接池 + */ + public boolean getCloseOfficialPool() { + return closeOfficialPool; + } + /** * FeishuClient构建器 */ public static class Builder { private final String appId; private final String appSecret; + private boolean closeOfficialPool = false; private OkHttpClient.Builder httpClientBuilder; private AppType appType = AppType.SELF_BUILT; private boolean logReqAtDebug = false; @@ -268,6 +289,11 @@ public class FeishuClient { return this; } + public Builder closeOfficialPool(boolean closeOfficialPool) { + this.closeOfficialPool = closeOfficialPool; + return this; + } + /** * 构建FeishuClient实例 * @@ -281,7 +307,7 @@ public class FeishuClient { // 构建OkHttpClient OkHttpClient httpClient = httpClientBuilder.build(); - return new FeishuClient(appId, appSecret, officialClient, httpClient); + return new FeishuClient(appId, appSecret, officialClient, httpClient, closeOfficialPool); } } } \ No newline at end of file diff --git a/src/main/java/cn/isliu/core/client/FsClient.java b/src/main/java/cn/isliu/core/client/FsClient.java index ed291d4..c5abe6a 100644 --- a/src/main/java/cn/isliu/core/client/FsClient.java +++ b/src/main/java/cn/isliu/core/client/FsClient.java @@ -9,6 +9,7 @@ import java.util.concurrent.ConcurrentHashMap; */ public class FsClient implements AutoCloseable { + private static volatile FsClient instance; private final ThreadLocal clientHolder = new ThreadLocal<>(); private final Map clientMap = new ConcurrentHashMap<>(); @@ -53,6 +54,10 @@ public class FsClient implements AutoCloseable { * @return 初始化的FeishuClient实例 */ public FeishuClient initializeClient(String appId, String appSecret) { + return initializeClient(appId, appSecret, false); + } + + public FeishuClient initializeClient(String appId, String appSecret, boolean closeOfficialPool) { if (appId == null || appId.trim().isEmpty()) { throw new IllegalArgumentException("appId cannot be null or empty"); } @@ -64,7 +69,7 @@ public class FsClient implements AutoCloseable { clientHolder.set(feishuClient); return feishuClient; } else { - FeishuClient client = FeishuClient.newBuilder(appId, appSecret).build(); + FeishuClient client = FeishuClient.newBuilder(appId, appSecret).closeOfficialPool(closeOfficialPool).build(); clientMap.put(appId + "_" + appSecret, client); clientHolder.set(client); return client; diff --git a/src/main/java/cn/isliu/core/utils/FsApiUtil.java b/src/main/java/cn/isliu/core/utils/FsApiUtil.java index fc1c1d6..8fd225e 100644 --- a/src/main/java/cn/isliu/core/utils/FsApiUtil.java +++ b/src/main/java/cn/isliu/core/utils/FsApiUtil.java @@ -14,14 +14,13 @@ import cn.isliu.core.service.*; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import com.lark.oapi.core.request.RequestOptions; import com.lark.oapi.service.drive.v1.model.*; import com.lark.oapi.service.sheets.v3.model.*; import java.io.IOException; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.concurrent.atomic.AtomicReference; -import cn.isliu.core.logging.FsLogger; import cn.isliu.core.enums.ErrorCode; @@ -33,19 +32,24 @@ import cn.isliu.core.enums.ErrorCode; public class FsApiUtil { private static final Gson gson = new Gson(); - private static final String REQ_TYPE = "JSON_STR"; public static final int DEFAULT_ROW_NUM = 1000; + public static final Map> m = new HashMap>() { + { + put("Connection", Collections.singletonList("close")); + } + }; + /** * 获取工作表数据 - *

+ * * 从指定的飞书表格中读取指定范围的数据 * - * @param sheetId 工作表ID + * @param sheetId 工作表ID * @param spreadsheetToken 电子表格Token - * @param startPosition 起始位置(如"A1") - * @param endPosition 结束位置(如"Z100") - * @param client 飞书客户端 + * @param startPosition 起始位置(如"A1") + * @param endPosition 结束位置(如"Z100") + * @param client 飞书客户端 * @return 表格数据对象 */ public static ValuesBatch getSheetData(String sheetId, String spreadsheetToken, String startPosition, String endPosition, FeishuClient client) { @@ -78,11 +82,11 @@ public class FsApiUtil { /** * 获取工作表元数据 - *

+ * * 获取指定工作表的元数据信息,包括行列数、工作表名称等 * - * @param sheetId 工作表ID - * @param client 飞书客户端 + * @param sheetId 工作表ID + * @param client 飞书客户端 * @param spreadsheetToken 电子表格Token * @return 工作表对象 */ @@ -92,8 +96,8 @@ public class FsApiUtil { .spreadsheetToken(spreadsheetToken) .build(); - // 发起请求 - QuerySpreadsheetSheetResp resp = client.sheets().v3().spreadsheetSheet().query(req); + QuerySpreadsheetSheetResp resp = client.sheets().v3().spreadsheetSheet().query(req, client.getCloseOfficialPool() + ? RequestOptions.newBuilder().headers(m).build() : null); // 处理服务端错误 if (resp.success()) { @@ -121,6 +125,8 @@ public class FsApiUtil { } public static void setTableStyle(CustomCellService.StyleCellsBatchBuilder styleCellsBatchBuilder, FeishuClient client, String spreadsheetToken) { + FsLogger.debug("【飞书表格】 写入表格样式参数:{}", gson.toJson(styleCellsBatchBuilder)); + try { CustomCellService.CellBatchUpdateRequest batchUpdateRequest = CustomCellService.CellBatchUpdateRequest.newBuilder() .addRequest(styleCellsBatchBuilder.build()) @@ -157,7 +163,7 @@ public class FsApiUtil { /** * 获取根目录Token - *

+ * * 调用飞书开放平台API获取当前租户的根目录token,用于后续的文件夹和文件操作 * API接口: GET https://open.feishu.cn/open-apis/drive/v1/files/root_folder/meta * @@ -195,7 +201,8 @@ public class FsApiUtil { .build(); // 发起请求 - CreateFolderFileResp resp = client.drive().v1().file().createFolder(req); + CreateFolderFileResp resp = client.drive().v1().file().createFolder(req, client.getCloseOfficialPool() + ? RequestOptions.newBuilder().headers(m).build() : null); if (resp.success()) { FsLogger.info("【飞书表格】 创建文件夹成功! {}", gson.toJson(resp)); return resp.getData(); @@ -218,7 +225,8 @@ public class FsApiUtil { .build()) .build(); - CreateSpreadsheetResp resp = client.sheets().v3().spreadsheet().create(req); + CreateSpreadsheetResp resp = client.sheets().v3().spreadsheet().create(req, client.getCloseOfficialPool() + ? RequestOptions.newBuilder().headers(m).build() : null); if (resp.success()) { FsLogger.info("【飞书表格】 创建表格成功! {}", gson.toJson(resp)); return resp.getData(); @@ -266,7 +274,7 @@ public class FsApiUtil { String message = e.getMessage(); FsLogger.warn("【飞书表格】 创建 sheet 异常!错误信息:{}", message); - throw new FsHelperException(message != null && message.contains("403") ? "请按照上方操作,当前智投无法操作对应文档哦" : "【飞书表格】 创建 sheet 异常!"); + throw new FsHelperException(message != null && message.contains("403")? "请按照上方操作,当前智投无法操作对应文档哦" : "【飞书表格】 创建 sheet 异常!"); } return sheetId; } @@ -387,7 +395,8 @@ public class FsApiUtil { .build(); // 发起请求 - DownloadMediaResp resp = client.drive().v1().media().download(req); + DownloadMediaResp resp = client.drive().v1().media().download(req, client.getCloseOfficialPool() + ? RequestOptions.newBuilder().headers(m).build() : null); // 处理服务端错误 if (resp.success()) { @@ -400,14 +409,15 @@ public class FsApiUtil { } } - public static String downloadTmpMaterialUrl(String fileToken, FeishuClient client) { + public static String downloadTmpMaterialUrl(String fileToken, FeishuClient client) { String tmpUrl = ""; try { BatchGetTmpDownloadUrlMediaReq req = BatchGetTmpDownloadUrlMediaReq.newBuilder() .fileTokens(new String[]{fileToken}) .build(); - BatchGetTmpDownloadUrlMediaResp resp = client.drive().v1().media().batchGetTmpDownloadUrl(req); + BatchGetTmpDownloadUrlMediaResp resp = client.drive().v1().media().batchGetTmpDownloadUrl(req, client.getCloseOfficialPool() + ? RequestOptions.newBuilder().headers(m).build() : null); if (resp.success()) { return resp.getData().getTmpDownloadUrls()[0].getTmpDownloadUrl(); @@ -421,7 +431,7 @@ public class FsApiUtil { } public static Object putValues(String spreadsheetToken, CustomValueService.ValueRequest putValuesBuilder, FeishuClient client) { - FsLogger.info("【飞书表格】 putValues 开始写入数据!参数:{}", gson.toJson(putValuesBuilder)); + FsLogger.debug("【飞书表格】 putValues 开始写入数据!参数:{}", gson.toJson(putValuesBuilder)); // 添加到批量请求中 CustomValueService.ValueBatchUpdateRequest putDataRequest = CustomValueService.ValueBatchUpdateRequest.newBuilder() @@ -466,7 +476,7 @@ public class FsApiUtil { } } - public static Object addRowColumns(String sheetId, String spreadsheetToken, String type, int length, FeishuClient client) { + public static Object addRowColumns(String sheetId, String spreadsheetToken, String type, int length,FeishuClient client) { CustomDimensionService.DimensionBatchUpdateRequest batchRequest = CustomDimensionService.DimensionBatchUpdateRequest.newBuilder() .addRequest(CustomDimensionService.DimensionRequest.addDimension() @@ -497,7 +507,8 @@ public class FsApiUtil { .build(); // 发起请求 - GetSpreadsheetResp resp = client.sheets().v3().spreadsheet().get(req); + GetSpreadsheetResp resp = client.sheets().v3().spreadsheet().get(req, client.getCloseOfficialPool() + ? RequestOptions.newBuilder().headers(m).build() : null); // 处理服务端错误 if (resp.success()) { @@ -512,13 +523,15 @@ public class FsApiUtil { } /** - * 字符串类型: formatter: "@" + * 字符串类型: formatter: "@" */ public static void setCellType(String sheetId, String formatter, String startPosition, String endPosition, FeishuClient client, String spreadsheetToken) { try { CustomCellService.CellBatchUpdateRequest batchUpdateRequest = CustomCellService.CellBatchUpdateRequest.newBuilder() .addRequest(CustomCellService.CellRequest.styleCells() .formatter(formatter).sheetId(sheetId).startPosition(startPosition).endPosition(endPosition) + .backColor("#ffffff") + .bold(false) .build()) .build(); @@ -533,7 +546,7 @@ public class FsApiUtil { } } - public static Object imageUpload(byte[] imageData, String fileName, String position, String sheetId, String spreadsheetToken, FeishuClient client) { + public static Object imageUpload(byte[] imageData, String fileName, String position ,String sheetId, String spreadsheetToken, FeishuClient client) { try { CustomValueService.ValueRequest imageRequest = CustomValueService.ValueRequest.imageValues() @@ -553,7 +566,7 @@ public class FsApiUtil { } return imageResp.getData(); } catch (Exception e) { - FsLogger.error(ErrorCode.API_SERVER_ERROR, "【飞书表格】 文件上传异常!" + e.getMessage(), fileName, e); + FsLogger.error(ErrorCode.API_SERVER_ERROR,"【飞书表格】 文件上传异常!" + e.getMessage(), fileName, e); } return null;