feat: FFI DTO V2 completion - add EventBufferStateV2 (DOC-FULL-007)

- 新增 EventBufferStateV2 结构体 (total/pending/exported/failed)
- 新增 get_event_buffer_state_v2() 方法
- FFI 导出 EventBufferStateV2 类型 + 方法
- FFI V2 exports 审计: 6 DTO + 9 方法全部到位
- V1 保留 deprecated,V2 接口显式带 V2 后缀

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-11 21:13:56 +08:00
parent 7f79802e0a
commit b08fe28525
2 changed files with 55 additions and 1 deletions

View File

@ -211,6 +211,28 @@ pub fn buffer_size_v2() -> usize {
buffer().lock().map(|b| b.len()).unwrap_or(0) buffer().lock().map(|b| b.len()).unwrap_or(0)
} }
/// Buffer state snapshot for diagnostic / debug use.
#[derive(Debug, Clone, uniffi::Record)]
pub struct EventBufferStateV2 {
pub total: u32,
pub pending: u32,
pub exported: u32,
pub failed: u32,
}
pub fn get_event_buffer_state_v2() -> EventBufferStateV2 {
match buffer().lock() {
Ok(buf) => {
let total = buf.len() as u32;
let pending = buf.iter().filter(|b| b.state == BufferedEventState::Pending).count() as u32;
let exported = buf.iter().filter(|b| b.state == BufferedEventState::Exported).count() as u32;
let failed = buf.iter().filter(|b| b.state == BufferedEventState::Failed).count() as u32;
EventBufferStateV2 { total, pending, exported, failed }
}
Err(_) => EventBufferStateV2 { total: 0, pending: 0, exported: 0, failed: 0 },
}
}
pub fn clear_all_events_v2() { pub fn clear_all_events_v2() {
if let Ok(mut buf) = buffer().lock() { if let Ok(mut buf) = buffer().lock() {
buf.clear(); buf.clear();
@ -608,6 +630,33 @@ mod tests {
teardown_session(&sid); teardown_session(&sid);
} }
#[test]
fn test_get_event_buffer_state() {
let _guard = lock_test();
clear_all_events_v2();
let sid = setup_session("mat_state");
let s0 = get_event_buffer_state_v2();
assert_eq!(s0.total, 0);
push_material_opened_v2(&sid, "mat_state", 1000).unwrap();
let s1 = get_event_buffer_state_v2();
assert_eq!(s1.total, 1);
assert_eq!(s1.pending, 1);
let exported = export_pending_events_v2(100, 2000);
assert!(!exported.is_empty());
let s2 = get_event_buffer_state_v2();
assert_eq!(s2.exported, 1);
assert_eq!(s2.pending, 0);
ack_events_v2(&[exported[0].event_id.clone()]);
let s3 = get_event_buffer_state_v2();
assert_eq!(s3.total, 0);
teardown_session(&sid);
}
#[test] #[test]
fn test_delta_semantics_per_event_type() { fn test_delta_semantics_per_event_type() {
let _guard = lock_test(); let _guard = lock_test();

View File

@ -14,7 +14,7 @@ pub use zx_document_core::events::ReadingEvent;
#[allow(deprecated)] #[allow(deprecated)]
pub use zx_document_core::reading_material::ReadingMaterialRef; pub use zx_document_core::reading_material::ReadingMaterialRef;
pub use zx_document_core::reading_material::ReadingMaterialRefV2; pub use zx_document_core::reading_material::ReadingMaterialRefV2;
pub use zx_document_core::events_v2::{ReadingEventV2, ReadingEventTypeV2}; pub use zx_document_core::events_v2::{ReadingEventV2, ReadingEventTypeV2, EventBufferStateV2};
pub use zx_document_core::epub::{EpubMetadata, EpubChapter}; pub use zx_document_core::epub::{EpubMetadata, EpubChapter};
pub use zx_document_core::office::{OfficePreviewConfig, OfficePreviewStrategy}; pub use zx_document_core::office::{OfficePreviewConfig, OfficePreviewStrategy};
pub use zx_document_core::pdf::{PdfMetadata, PdfPageText}; pub use zx_document_core::pdf::{PdfMetadata, PdfPageText};
@ -114,6 +114,11 @@ fn reload_stale_events_v2() -> u32 {
zx_document_core::events_v2::reload_stale_events_v2() zx_document_core::events_v2::reload_stale_events_v2()
} }
#[uniffi::export]
fn get_event_buffer_state_v2() -> EventBufferStateV2 {
zx_document_core::events_v2::get_event_buffer_state_v2()
}
#[uniffi::export] #[uniffi::export]
fn ack_events_v2(event_ids: Vec<String>) -> u32 { fn ack_events_v2(event_ids: Vec<String>) -> u32 {
zx_document_core::events_v2::ack_events_v2(&event_ids) zx_document_core::events_v2::ack_events_v2(&event_ids)