Skip to content

Commit 6268f34

Browse files
committed
refactor(flashcards): 拆行映射域恢复可读并补壳注释
1 parent 869b9f0 commit 6268f34

3 files changed

Lines changed: 69 additions & 26 deletions

File tree

‎app/src-tauri/src/commands_flashcards.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ pub fn count_due_cards(state: State<'_, AppState>, group_id: Option<i64>) -> Res
163163
}
164164

165165
/// 复习评分(提取优先闭环:front→回忆→back→评分→调度推进)。
166+
///
167+
/// @ai-context: IPC 命令壳——只解引用 State 后委托 `review_card_inner`(仓内约定
168+
/// 「inner 等价于测全命令」);入参校验/调度/落库/埋点全在 inner。
166169
#[tauri::command]
167170
pub fn review_card(
168171
state: State<'_, AppState>,

‎app/src-tauri/src/db_flashcards.rs‎

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ use crate::db::{unix_seconds, Db};
1010
use crate::error::Result;
1111
use crate::types::Flashcard;
1212

13-
/// flashcards 表统一查询列(列顺序与 row_to_card 严格对应)。
14-
const CARD_COLUMNS: &str =
15-
"id, group_id, note_id, fragment_id, front, back, kind, state_json, due_at, created_at";
13+
/// 行映射域(列序契约 + ②域行派生间隔)——见 db_flashcards_interval.rs 模块头。
14+
#[path = "db_flashcards_interval.rs"]
15+
mod db_flashcards_interval;
16+
17+
use db_flashcards_interval::{row_to_card, CARD_COLUMNS};
1618

1719
/// 新建闪卡入参(id/created_at 由数据层填充;新卡 state_json=CardState::default 序列化)。
1820
pub struct NewFlashcard {
@@ -271,29 +273,6 @@ impl Db {
271273
}
272274
}
273275

274-
/// 把 rusqlite 行映射为 Flashcard。
275-
fn row_to_card(row: &rusqlite::Row<'_>) -> rusqlite::Result<Flashcard> {
276-
Ok(Flashcard {
277-
id: row.get(0)?,
278-
group_id: row.get(1)?,
279-
note_id: row.get(2)?,
280-
fragment_id: row.get(3)?,
281-
front: row.get(4)?,
282-
back: row.get(5)?,
283-
kind: row.get(6)?,
284-
state_json: row.get(7)?,
285-
due_at: row.get(8)?,
286-
created_at: row.get(9)?,
287-
interval_days: interval_days_from(row.get(8)?, &row.get::<_, String>(7)?),
288-
})
289-
}
290-
/// 行派生间隔(整天粒度):`due_at − stateJson.lastReviewMs` 反推;无复习记录/劣化输入 ⇒ 0.0。
291-
fn interval_days_from(due_at: i64, state_json: &str) -> f32 {
292-
let s: crate::scheduler::CardState = serde_json::from_str(state_json).unwrap_or_default();
293-
let days = (due_at.saturating_sub(s.last_review_ms as i64)) as f64 / 86_400_000.0;
294-
if s.last_review_ms == 0 || days <= 0.0 { 0.0 } else { days.round().max(1.0) as f32 }
295-
}
296-
297276
/// 单测独立文件。
298277
#[cfg(test)]
299278
#[path = "db_flashcards_tests.rs"]
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
//! flashcards 行映射域(列序契约 `CARD_COLUMNS` + ②域行派生 intervalDays)。
2+
//!
3+
//! @ai-context: 从 db_flashcards.rs 拆出(批 6 · T20b,原 300/300 零余量)——
4+
//! `CARD_COLUMNS` 与 `row_to_card` 是同一列序契约的两面(5 个查询
5+
//! 方法共用),`interval_days_from` 是 PB2 裁决**②域(整天粒度)**的
6+
//! 唯一实现:由 `due_at − stateJson.lastReviewMs` 反推(DB 无 interval
7+
//! 列,f32 原值不可恢复)。**①域(精确值)不在此处**——它只在
8+
//! commands_flashcards.rs 的 review_card 返回体里当场显式覆写。
9+
//! @ai-context: 副作用边界——本文件只读行、不写库、不含 SQL 文本(SQL 仍全在
10+
//! db_flashcards.rs);劣化输入(无复习记录 / lastReviewMs ≥ due_at)
11+
//! ⇒ `0.0`(不发明数字、不 panic;确切时刻看 `dueAt`)。
12+
13+
use crate::scheduler::CardState;
14+
use crate::types::Flashcard;
15+
16+
/// flashcards 表统一查询列(列顺序与 row_to_card 严格对应)。
17+
pub(crate) const CARD_COLUMNS: &str =
18+
"id, group_id, note_id, fragment_id, front, back, kind, state_json, due_at, created_at";
19+
20+
/// 把 rusqlite 行映射为 Flashcard(列序契约的唯一消费点)。
21+
///
22+
/// 列序 0..9 **各读一次**(读序与拆分前逐字相同),再整体装配——`state_json` /
23+
/// `due_at` 先落到局部量,②域行派生直接复用,不再对同一列二次取值。
24+
pub(crate) fn row_to_card(row: &rusqlite::Row<'_>) -> rusqlite::Result<Flashcard> {
25+
let id: i64 = row.get(0)?;
26+
let group_id: i64 = row.get(1)?;
27+
let note_id: Option<i64> = row.get(2)?;
28+
let fragment_id: Option<i64> = row.get(3)?;
29+
let front: String = row.get(4)?;
30+
let back: String = row.get(5)?;
31+
let kind: String = row.get(6)?;
32+
let state_json: String = row.get(7)?;
33+
let due_at: i64 = row.get(8)?;
34+
let created_at: i64 = row.get(9)?;
35+
let interval_days = interval_days_from(due_at, &state_json);
36+
Ok(Flashcard {
37+
id,
38+
group_id,
39+
note_id,
40+
fragment_id,
41+
front,
42+
back,
43+
kind,
44+
state_json,
45+
due_at,
46+
created_at,
47+
interval_days,
48+
})
49+
}
50+
51+
/// 行派生间隔(**②域 · 整天粒度**):`due_at − stateJson.lastReviewMs` 反推;
52+
/// 无复习记录 / 劣化输入 ⇒ `0.0`(不发明数字;确切时刻看 `dueAt`)。
53+
fn interval_days_from(due_at: i64, state_json: &str) -> f32 {
54+
let s: CardState = serde_json::from_str(state_json).unwrap_or_default();
55+
let days = (due_at.saturating_sub(s.last_review_ms as i64)) as f64 / 86_400_000.0;
56+
if s.last_review_ms == 0 || days <= 0.0 {
57+
0.0
58+
} else {
59+
days.round().max(1.0) as f32
60+
}
61+
}

0 commit comments

Comments
 (0)