async function handleSync(dataSourceId: string) {
// 1. 从数据库读取数据源配置
const dataSource = await supabase
.from("data_sources").select("*")
.eq("id", dataSourceId).single();
// 2. 获取飞书访问令牌
const accessToken = await getFeishuTenantToken(
dataSource.feishu_app_id,
dataSource.feishu_app_secret
);
// 3. 获取字段定义(用于 column_headers)
const fields = await listBitableFields(
accessToken,
dataSource.spreadsheet_token, // 存储的是 app_token
dataSource.sheet_id // 存储的是 table_id
);
// 4. 分页拉取全部记录
const { records } = await listBitableRecords(
accessToken,
dataSource.spreadsheet_token,
dataSource.sheet_id,
dataSource.data_range || undefined // 可选的 view_id
);
// 5. 转换记录格式,提取显示值
const rows = records.map((record, index) => ({
data_source_id: dataSourceId,
row_index: index,
row_data: Object.fromEntries(
fields.map(f => [f.field_name, extractDisplayValue(record.fields[f.field_name])])
),
}));
// 6. 写入数据库(先删后插)
await supabase.from("synced_rows").delete().eq("data_source_id", dataSourceId);
// 分批插入,每批 500 条
for (let i = 0; i < rows.length; i += 500) {
await supabase.from("synced_rows").insert(rows.slice(i, i + 500));
}
// 7. 更新数据源状态 + 写入同步日志
await supabase.from("data_sources").update({
status: "active",
row_count: records.length,
column_headers: fields.map((f, i) => ({ index: i, name: f.field_name })),
last_synced_at: new Date().toISOString(),
}).eq("id", dataSourceId);
await supabase.from("sync_logs").insert({
data_source_id: dataSourceId,
status: "success",
rows_synced: records.length,
rows_added: addedCount,
rows_updated: updatedCount,
rows_deleted: deletedCount,
// ...
});
}