use open_lark::prelude::*;
use open_lark::{
core::trait_system::ExecutableBuilder,
service::cloud_docs::bitable::v1::app_table_record::search::*,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenvy::dotenv().ok();
let app_id = std::env::var("APP_ID").expect("APP_ID environment variable not set");
let app_secret = std::env::var("APP_SECRET").expect("APP_SECRET environment variable not set");
let app_token =
std::env::var("APP_TOKEN").unwrap_or_else(|_| "bascnCMII2ORuEjIDXvVecCKNEc".to_string()); let table_id = std::env::var("TABLE_ID").unwrap_or_else(|_| "tblsRc9GRRXKqhvW".to_string());
let client = LarkClient::builder(&app_id, &app_secret)
.with_enable_token_cache(true)
.build();
println!("🗃️ 飞书多维表格记录查询示例");
println!("应用Token: {app_token}");
println!("表格ID: {table_id}");
println!("{}", "=".repeat(50));
query_all_records(&client, &app_token, &table_id).await?;
query_with_filter(&client, &app_token, &table_id).await?;
query_with_sort_and_pagination(&client, &app_token, &table_id).await?;
Ok(())
}
async fn query_all_records(
client: &LarkClient,
app_token: &str,
table_id: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("\n📋 查询所有记录...");
match SearchRecordRequest::builder()
.app_token(app_token)
.table_id(table_id)
.page_size(20)
.automatic(true) .execute(&client.bitable.v1.app_table_record)
.await
{
Ok(response) => {
if let Some(data) = &response.data {
println!("✅ 记录查询成功!");
println!(" 总记录数: {}", data.items.len());
println!(" 是否有更多: {}", data.has_more);
if !data.items.is_empty() {
println!("\n📄 记录列表:");
for (index, record) in data.items.iter().enumerate() {
println!(
" {}. 记录ID: {}",
index + 1,
record.record_id.as_ref().unwrap_or(&"N/A".to_string())
);
if let Some(created_time) = &record.created_time {
println!(" 创建时间: {created_time}");
}
if let Some(modified_time) = &record.last_modified_time {
println!(" 修改时间: {modified_time}");
}
if !record.fields.is_empty() {
println!(" 字段数据:");
for (field_name, value) in &record.fields {
let display_value = format_field_value(value);
println!(" {field_name}: {display_value}");
}
}
println!(); }
} else {
println!("📭 表格为空,没有记录");
}
if data.has_more {
println!("💡 提示: 还有更多记录可以通过分页获取");
if let Some(page_token) = &data.page_token {
println!(" 下一页Token: {page_token}");
}
}
} else {
println!("⚠️ 请求成功,但未返回数据");
}
}
Err(e) => {
println!("❌ 查询记录失败: {e:?}");
println!("\n💡 常见错误解决方案:");
println!(" 1. 检查APP_ID和APP_SECRET是否正确");
println!(" 2. 确认APP_TOKEN是否为有效的多维表格应用token");
println!(" 3. 验证TABLE_ID是否正确");
println!(" 4. 确保应用有多维表格的读取权限");
return Err(e.into());
}
}
Ok(())
}
async fn query_with_filter(
client: &LarkClient,
app_token: &str,
table_id: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("\n🔍 带筛选条件查询...");
let filter = FilterInfo {
conjunction: "and".to_string(),
conditions: vec![FilterCondition {
field_name: "名称".to_string(), operator: "contains".to_string(),
value: Some(vec!["测试".to_string()]),
}],
};
match SearchRecordRequest::builder()
.app_token(app_token)
.table_id(table_id)
.filter(filter)
.page_size(10)
.execute(&client.bitable.v1.app_table_record)
.await
{
Ok(response) => {
if let Some(data) = &response.data {
println!("✅ 筛选查询成功!");
println!(" 筛选后记录数: {}", data.items.len());
if !data.items.is_empty() {
println!("\n📋 筛选结果:");
for (index, record) in data.items.iter().enumerate() {
println!(
" {}. 记录ID: {}",
index + 1,
record.record_id.as_ref().unwrap_or(&"N/A".to_string())
);
if let Some(name_value) = record.fields.get("名称") {
println!(" 名称: {}", format_field_value(name_value));
}
}
} else {
println!("📭 没有匹配筛选条件的记录");
}
}
}
Err(e) => {
println!("❌ 筛选查询失败: {e:?}");
println!(" 注意: 筛选字段名需要与实际表格字段匹配");
}
}
Ok(())
}
async fn query_with_sort_and_pagination(
client: &LarkClient,
app_token: &str,
table_id: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("\n📊 带排序的分页查询...");
let sort_conditions = vec![SortCondition {
field_name: "创建时间".to_string(), desc: Some(true), }];
let mut page_count = 0;
let mut page_token: Option<String> = None;
loop {
page_count += 1;
println!("\n📄 获取第 {page_count} 页...");
let mut request_builder = SearchRecordRequest::builder()
.app_token(app_token)
.table_id(table_id)
.sort(sort_conditions.clone())
.page_size(5) .automatic(false);
if let Some(token) = &page_token {
request_builder = request_builder.page_token(token);
}
match request_builder
.execute(&client.bitable.v1.app_table_record)
.await
{
Ok(response) => {
if let Some(data) = &response.data {
println!(" 本页记录数: {}", data.items.len());
for record in &data.items {
println!(
" - 记录ID: {}",
record.record_id.as_ref().unwrap_or(&"N/A".to_string())
);
if let Some(time_value) = record.fields.get("创建时间") {
println!(" 创建时间: {}", format_field_value(time_value));
}
}
if data.has_more {
page_token = data.page_token.clone();
println!(" → 还有更多页面...");
if page_count >= 3 {
println!(" ⏹️ 演示限制:最多显示3页");
break;
}
} else {
println!(" ✅ 已获取所有记录");
break;
}
} else {
println!(" ⚠️ 本页无数据");
break;
}
}
Err(e) => {
println!(" ❌ 第{page_count}页获取失败: {e:?}");
break;
}
}
}
println!("\n📈 分页查询总结:");
println!(" 总页数: {page_count}");
Ok(())
}
fn format_field_value(value: &serde_json::Value) -> String {
match value {
serde_json::Value::String(s) => s.clone(),
serde_json::Value::Number(n) => n.to_string(),
serde_json::Value::Bool(b) => b.to_string(),
serde_json::Value::Array(arr) => {
format!("[{}]", arr.len())
}
serde_json::Value::Object(_) => "[对象]".to_string(),
serde_json::Value::Null => "[空]".to_string(),
}
}
#[allow(dead_code)]
fn build_complex_filter() -> FilterInfo {
FilterInfo {
conjunction: "and".to_string(),
conditions: vec![
FilterCondition {
field_name: "状态".to_string(),
operator: "is".to_string(),
value: Some(vec!["进行中".to_string()]),
},
FilterCondition {
field_name: "优先级".to_string(),
operator: "isGreater".to_string(),
value: Some(vec!["2".to_string()]),
},
FilterCondition {
field_name: "创建时间".to_string(),
operator: "isAfter".to_string(),
value: Some(vec!["2024-01-01".to_string()]),
},
],
}
}