feat(feishu): 添加关闭官方Client连接池功能

- 在FeishuClient中新增closeOfficialPool配置项,用于控制是否关闭官方SDK的连接池- 新增Builder模式下的closeOfficialPool方法,支持链式调用设置该选项
- 修改FsApiUtil工具类,在调用飞书API时根据配置决定是否添加Connection: close头部
- 更新FsClient初始化逻辑,支持传入closeOfficialPool参数并传递给FeishuClient
- 调整部分日志级别,将info级别改为debug以减少生产环境日志量
-优化代码格式,移除不必要的空行和注释中的多余换行符号
This commit is contained in:
liushuang 2025-10-15 14:21:12 +08:00
parent e1b1d5cd01
commit 9500a1df12
3 changed files with 73 additions and 29 deletions

@ -22,6 +22,7 @@ public class FeishuClient {
private final OkHttpClient httpClient; private final OkHttpClient httpClient;
private final String appId; private final String appId;
private final String appSecret; private final String appSecret;
private final boolean closeOfficialPool;
// 自定义服务处理官方SDK未覆盖的API // 自定义服务处理官方SDK未覆盖的API
private volatile CustomSheetService customSheetService; private volatile CustomSheetService customSheetService;
@ -37,8 +38,18 @@ public class FeishuClient {
this.appSecret = appSecret; this.appSecret = appSecret;
this.officialClient = officialClient; this.officialClient = officialClient;
this.httpClient = httpClient; 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; return appSecret;
} }
/**
* 获取是否关闭官方Client连接池
*
* @return 是否关闭官方Client连接池
*/
public boolean getCloseOfficialPool() {
return closeOfficialPool;
}
/** /**
* FeishuClient构建器 * FeishuClient构建器
*/ */
public static class Builder { public static class Builder {
private final String appId; private final String appId;
private final String appSecret; private final String appSecret;
private boolean closeOfficialPool = false;
private OkHttpClient.Builder httpClientBuilder; private OkHttpClient.Builder httpClientBuilder;
private AppType appType = AppType.SELF_BUILT; private AppType appType = AppType.SELF_BUILT;
private boolean logReqAtDebug = false; private boolean logReqAtDebug = false;
@ -268,6 +289,11 @@ public class FeishuClient {
return this; return this;
} }
public Builder closeOfficialPool(boolean closeOfficialPool) {
this.closeOfficialPool = closeOfficialPool;
return this;
}
/** /**
* 构建FeishuClient实例 * 构建FeishuClient实例
* *
@ -281,7 +307,7 @@ public class FeishuClient {
// 构建OkHttpClient // 构建OkHttpClient
OkHttpClient httpClient = httpClientBuilder.build(); OkHttpClient httpClient = httpClientBuilder.build();
return new FeishuClient(appId, appSecret, officialClient, httpClient); return new FeishuClient(appId, appSecret, officialClient, httpClient, closeOfficialPool);
} }
} }
} }

@ -9,6 +9,7 @@ import java.util.concurrent.ConcurrentHashMap;
*/ */
public class FsClient implements AutoCloseable { public class FsClient implements AutoCloseable {
private static volatile FsClient instance; private static volatile FsClient instance;
private final ThreadLocal<FeishuClient> clientHolder = new ThreadLocal<>(); private final ThreadLocal<FeishuClient> clientHolder = new ThreadLocal<>();
private final Map<String, FeishuClient> clientMap = new ConcurrentHashMap<>(); private final Map<String, FeishuClient> clientMap = new ConcurrentHashMap<>();
@ -53,6 +54,10 @@ public class FsClient implements AutoCloseable {
* @return 初始化的FeishuClient实例 * @return 初始化的FeishuClient实例
*/ */
public FeishuClient initializeClient(String appId, String appSecret) { 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()) { if (appId == null || appId.trim().isEmpty()) {
throw new IllegalArgumentException("appId cannot be null or empty"); throw new IllegalArgumentException("appId cannot be null or empty");
} }
@ -64,7 +69,7 @@ public class FsClient implements AutoCloseable {
clientHolder.set(feishuClient); clientHolder.set(feishuClient);
return feishuClient; return feishuClient;
} else { } else {
FeishuClient client = FeishuClient.newBuilder(appId, appSecret).build(); FeishuClient client = FeishuClient.newBuilder(appId, appSecret).closeOfficialPool(closeOfficialPool).build();
clientMap.put(appId + "_" + appSecret, client); clientMap.put(appId + "_" + appSecret, client);
clientHolder.set(client); clientHolder.set(client);
return client; return client;

@ -14,14 +14,13 @@ import cn.isliu.core.service.*;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; 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.drive.v1.model.*;
import com.lark.oapi.service.sheets.v3.model.*; import com.lark.oapi.service.sheets.v3.model.*;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.*;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import cn.isliu.core.logging.FsLogger;
import cn.isliu.core.enums.ErrorCode; import cn.isliu.core.enums.ErrorCode;
@ -33,12 +32,17 @@ import cn.isliu.core.enums.ErrorCode;
public class FsApiUtil { public class FsApiUtil {
private static final Gson gson = new Gson(); 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 int DEFAULT_ROW_NUM = 1000;
public static final Map<String, List<String>> m = new HashMap<String, List<String>>() {
{
put("Connection", Collections.singletonList("close"));
}
};
/** /**
* 获取工作表数据 * 获取工作表数据
* <p> *
* 从指定的飞书表格中读取指定范围的数据 * 从指定的飞书表格中读取指定范围的数据
* *
* @param sheetId 工作表ID * @param sheetId 工作表ID
@ -78,7 +82,7 @@ public class FsApiUtil {
/** /**
* 获取工作表元数据 * 获取工作表元数据
* <p> *
* 获取指定工作表的元数据信息包括行列数工作表名称等 * 获取指定工作表的元数据信息包括行列数工作表名称等
* *
* @param sheetId 工作表ID * @param sheetId 工作表ID
@ -92,8 +96,8 @@ public class FsApiUtil {
.spreadsheetToken(spreadsheetToken) .spreadsheetToken(spreadsheetToken)
.build(); .build();
// 发起请求 QuerySpreadsheetSheetResp resp = client.sheets().v3().spreadsheetSheet().query(req, client.getCloseOfficialPool()
QuerySpreadsheetSheetResp resp = client.sheets().v3().spreadsheetSheet().query(req); ? RequestOptions.newBuilder().headers(m).build() : null);
// 处理服务端错误 // 处理服务端错误
if (resp.success()) { if (resp.success()) {
@ -121,6 +125,8 @@ public class FsApiUtil {
} }
public static void setTableStyle(CustomCellService.StyleCellsBatchBuilder styleCellsBatchBuilder, FeishuClient client, String spreadsheetToken) { public static void setTableStyle(CustomCellService.StyleCellsBatchBuilder styleCellsBatchBuilder, FeishuClient client, String spreadsheetToken) {
FsLogger.debug("【飞书表格】 写入表格样式参数:{}", gson.toJson(styleCellsBatchBuilder));
try { try {
CustomCellService.CellBatchUpdateRequest batchUpdateRequest = CustomCellService.CellBatchUpdateRequest.newBuilder() CustomCellService.CellBatchUpdateRequest batchUpdateRequest = CustomCellService.CellBatchUpdateRequest.newBuilder()
.addRequest(styleCellsBatchBuilder.build()) .addRequest(styleCellsBatchBuilder.build())
@ -157,7 +163,7 @@ public class FsApiUtil {
/** /**
* 获取根目录Token * 获取根目录Token
* <p> *
* 调用飞书开放平台API获取当前租户的根目录token用于后续的文件夹和文件操作 * 调用飞书开放平台API获取当前租户的根目录token用于后续的文件夹和文件操作
* API接口: GET https://open.feishu.cn/open-apis/drive/v1/files/root_folder/meta * API接口: GET https://open.feishu.cn/open-apis/drive/v1/files/root_folder/meta
* *
@ -195,7 +201,8 @@ public class FsApiUtil {
.build(); .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()) { if (resp.success()) {
FsLogger.info("【飞书表格】 创建文件夹成功! {}", gson.toJson(resp)); FsLogger.info("【飞书表格】 创建文件夹成功! {}", gson.toJson(resp));
return resp.getData(); return resp.getData();
@ -218,7 +225,8 @@ public class FsApiUtil {
.build()) .build())
.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()) { if (resp.success()) {
FsLogger.info("【飞书表格】 创建表格成功! {}", gson.toJson(resp)); FsLogger.info("【飞书表格】 创建表格成功! {}", gson.toJson(resp));
return resp.getData(); return resp.getData();
@ -387,7 +395,8 @@ public class FsApiUtil {
.build(); .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()) { if (resp.success()) {
@ -407,7 +416,8 @@ public class FsApiUtil {
.fileTokens(new String[]{fileToken}) .fileTokens(new String[]{fileToken})
.build(); .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()) { if (resp.success()) {
return resp.getData().getTmpDownloadUrls()[0].getTmpDownloadUrl(); return resp.getData().getTmpDownloadUrls()[0].getTmpDownloadUrl();
@ -421,7 +431,7 @@ public class FsApiUtil {
} }
public static Object putValues(String spreadsheetToken, CustomValueService.ValueRequest putValuesBuilder, FeishuClient client) { 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() CustomValueService.ValueBatchUpdateRequest putDataRequest = CustomValueService.ValueBatchUpdateRequest.newBuilder()
@ -497,7 +507,8 @@ public class FsApiUtil {
.build(); .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()) { if (resp.success()) {
@ -519,6 +530,8 @@ public class FsApiUtil {
CustomCellService.CellBatchUpdateRequest batchUpdateRequest = CustomCellService.CellBatchUpdateRequest.newBuilder() CustomCellService.CellBatchUpdateRequest batchUpdateRequest = CustomCellService.CellBatchUpdateRequest.newBuilder()
.addRequest(CustomCellService.CellRequest.styleCells() .addRequest(CustomCellService.CellRequest.styleCells()
.formatter(formatter).sheetId(sheetId).startPosition(startPosition).endPosition(endPosition) .formatter(formatter).sheetId(sheetId).startPosition(startPosition).endPosition(endPosition)
.backColor("#ffffff")
.bold(false)
.build()) .build())
.build(); .build();