English: README.en.md
基于 飞书开放平台 Java SDK(com.larksuite.oapi:oapi-sdk)的扩展库:在官方 API 之上提供多维表格(Bitable)的实体映射、查询封装、远程配置缓存、事件分发,以及消息卡片与用户查询等常用能力,减少手写 JSON 与重复样板代码。
License: MIT
| 能力 | 说明 |
|---|---|
实体仓库 BitableRepository |
将 Java 类字段通过 @Property 映射到表格列名;结合远端元数据解析列类型,完成与飞书 API 之间的序列化/反序列化。 |
| CRUD 与查询 | 实现 IBitableOperation:getById、queryByIds、query / queryPage(支持 FilterInfo、Sort 与链式 Conditions)、insert、update、insertOrUpdate、delete、batchDelete。 |
| 字段类型编解码 | 内置多种列类型的 IFeishuFieldTypeMarshaller(文本、数字、日期时间、人员、单选/多选、复选、附件、URL、公式、自动编号、关联等),可通过 @Property(marshaller = …) 自定义。 |
| 关联表 | 关联列由远端元数据解析关联表 ID,库内自动创建子表 BitableRepository;实体字段类型声明为关联行的 BitableRecord(或集合)即可,无需手动创建子表仓库。 |
条件构建 Conditions |
流式 API 构建筛选条件(如 Conditions.field("列名").equal("值"),并支持 and() / or() 组合)。 |
分页 Page |
对搜索结果做分页抽象,便于遍历多页数据。 |
键值存储 BitableKeyValueStore |
在 Bitable 之上提供 IKeyValueStore 语义(Map / Table 两种模式),适合简单配置或键值场景。 |
远程配置 BitableRemoteConfig |
按 Conditions 将符合条件的行加载到内存缓存,可定时刷新;可与下方「记录变更」联动做失效或更新。 |
记录变更 BitableRecordChangeDispatcher |
订阅云文档中多维表格文件级变更事件,按 appToken 分发给 IRecordChangeListener(例如驱动 BitableRemoteConfig 刷新)。 |
| 能力 | 说明 |
|---|---|
CardSender / CardTemplateBuilder |
通过 IM 接口发送交互式卡片:将数据对象序列化为模板变量 JSON(需配置模板 ID 与版本)。 |
LarkUserQuery |
通讯录辅助:按 Open ID 查用户、按邮箱解析用户等(Optional 返回)。 |
| 异常 | LarkApiException、BitableOperateException 等统一封装调用失败场景。 |
- 运行时:
oapi-sdk、gson(版本见根目录build.gradle)。 - 构建:Gradle(自带 Wrapper:
./gradlew/gradlew.bat)。 - 测试:JUnit 5、Mockito。
本地构建与测试:
./gradlew build(Windows 可使用 gradlew.bat build。)
发布坐标(build.gradle 中定义):
- Group:
io.github.oioiovo - Artifact:
lark-api-extensions - Version: 以仓库中
version为准(例如0.1.0)
Gradle:
dependencies {
implementation 'io.github.oioiovo:lark-api-extensions:0.1.0'
}Maven:
<dependency>
<groupId>io.github.oioiovo</groupId>
<artifactId>lark-api-extensions</artifactId>
<version>0.1.0</version>
</dependency>若已发布至 Maven Central,可在 search.maven.org 搜索上述坐标确认最新版本。
以下示例为说明用法;实际 appToken、tableId、列名、模板 ID 等需替换为你环境中的真实值。应用需开通对应权限(如多维表格、通讯录、IM、云文档事件订阅等)。
使用飞书官方 Java SDK 创建 com.lark.oapi.Client(凭证勿提交到仓库):
import com.lark.oapi.Client;
Client client = Client.newBuilder()
.appId("cli_xxxxxxxx")
.appSecret("xxxxxxxxxxxxxxxx")
.build();实体类需有无参构造,实现 BitableRecord,并用 @Property 与表格列名一致;getRecordId / setRecordId 由 BitableRepository 在读写时维护。
import io.github.oioiovo.lark.bitable.repository.BitableRecord;
import io.github.oioiovo.lark.bitable.repository.BitableRepository;
import io.github.oioiovo.lark.bitable.repository.annotation.Property;
import io.github.oioiovo.lark.bitable.repository.condition.Conditions;
public class TaskRow implements BitableRecord {
private String recordId;
@Property("标题")
private String title;
public TaskRow() {
}
@Override
public String getRecordId() {
return recordId;
}
@Override
public void setRecordId(String recordId) {
this.recordId = recordId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
// 使用
BitableRepository<TaskRow> repo = new BitableRepository<>(
client,
"多维表格 app_token",
"数据表 table_id",
TaskRow.class);
// 按条件查询(会拉取所有分页)
Conditions filter = Conditions.field("状态").equal("进行中");
java.util.List<TaskRow> list = repo.query(filter);
// 新增一行
TaskRow row = new TaskRow();
row.setTitle("新任务");
String newId = repo.insert(row);
if (newId != null) {
row.setRecordId(newId); // insert 返回新 record_id,需自行写回实体
}若存在「关联」列,在实体类中将字段类型声明为关联表对应的 BitableRecord 实现类即可(与列类型一致);BitableRepository 会根据元数据自动创建子表仓库并完成编解码。仅在需要复用或自定义子表仓库时,才在构造时额外传入已有的 BitableRepository(见源码 JavaDoc)。
需先在飞书搭建卡片模板,拿到 template_id 与 template_version_name。
import com.lark.oapi.service.im.v1.enums.CreateMessageReceiveIdTypeEnum;
import io.github.oioiovo.lark.card.CardSender;
CardSender sender = new CardSender(client, "模板 ID", "模板版本名");
// 任意可序列化为模板变量的对象(如 Map、POJO)
java.util.Map<String, String> vars = new java.util.HashMap<>();
vars.put("title", "hello");
sender.send(vars, CreateMessageReceiveIdTypeEnum.CHAT_ID, "oc_xxxxxxxx");import io.github.oioiovo.lark.user.LarkUserQuery;
LarkUserQuery users = new LarkUserQuery(client);
users.findById("ou_xxxxxxxx").ifPresent(u -> System.out.println(u.getName()));
users.findByEmail("user@example.com").ifPresent(u -> System.out.println(u.getOpenId()));BitableRecordChangeDispatcher 需在可接收飞书事件回调的环境中注册:将其实例作为 P2FileBitableRecordChangedV1 的处理器挂到官方 EventDispatcher(或等价 HTTP 长连接/回调)上,具体以 飞书事件文档 为准。
BitableRemoteConfig 适合把一张「配置表」缓存在内存,支持按条件筛选、定时刷新,以及可选地绑定到上面的 dispatcher,在表格变更时刷新或剔除缓存。
import io.github.oioiovo.lark.bitable.config.BitableRemoteConfig;
import io.github.oioiovo.lark.bitable.event.BitableRecordChangeDispatcher;
import io.github.oioiovo.lark.bitable.repository.annotation.Property;
import io.github.oioiovo.lark.bitable.repository.condition.Conditions;
import java.util.concurrent.TimeUnit;
public class ConfigRow implements BitableRecord {
private String recordId;
@Property("键")
private String configKey;
@Property("值")
private String configValue;
public ConfigRow() {
}
@Override
public String getRecordId() {
return recordId;
}
@Override
public void setRecordId(String recordId) {
this.recordId = recordId;
}
// getter / setter for configKey, configValue ...
}
BitableRecordChangeDispatcher dispatcher = new BitableRecordChangeDispatcher(client);
// 在事件订阅中注册 dispatcher 处理 bitable 记录变更事件后,再执行:
try (BitableRemoteConfig<ConfigRow> remoteConfig = BitableRemoteConfig.newBuilder(
client, "app_token", "table_id", ConfigRow.class)
.withConditions(Conditions.field("环境").equal("prod"))
.withRefreshInterval(5, TimeUnit.MINUTES)
.withDispatcher(dispatcher)
.build()) {
java.util.List<ConfigRow> snapshot = remoteConfig.read();
// 使用 snapshot ...
}更细的 API 说明以源码中的 JavaDoc 为准。
欢迎 Issue / PR。使用本库时请遵守飞书开放平台的服务条款与调用规范。