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,
// ...
});
}