@@ -24,6 +24,9 @@ mod plan;
2424/// 目标域:goals 实体 CRUD(含事务建目标)+ goals 行映射。
2525#[ path = "db_goals_goal.rs" ]
2626mod goal;
27+ /// 回顾域:结算快照 / 复习统计 / 成果物清单聚合取数。
28+ #[ path = "db_goals_retro.rs" ]
29+ mod retro;
2730
2831/// 三表 DDL + 索引(幂等:CREATE TABLE IF NOT EXISTS;旧库升级自动补表)。
2932pub ( crate ) fn init ( conn : & Connection ) -> Result < ( ) > {
@@ -167,92 +170,6 @@ impl Db {
167170 rows. collect :: < rusqlite:: Result < Vec < _ > > > ( ) . map_err ( Into :: into)
168171 } )
169172 }
170-
171- /// 组结算快照(绑定组维度:名称/历史计数/最近一次——毕业报告与时间线)。
172- /// @ai-context: group_ids 先于 with_conn 取(闭包内严禁再调 self.*——锁不可重入,
173- /// 见 db_goals_progress.rs 同款注释与修复先例)。
174- pub fn goal_settlements_snapshot (
175- & self ,
176- goal_id : i64 ,
177- ) -> Result < Vec < crate :: goal_retro:: GroupSettlementSnapshot > > {
178- let group_ids = self . list_goal_group_ids ( goal_id) ?;
179- self . with_conn ( |conn| {
180- let mut out = Vec :: new ( ) ;
181- for gid in & group_ids {
182- let name: Option < String > = conn
183- . query_row ( "SELECT name FROM note_groups WHERE id = ?1" , params ! [ gid] , |r| r. get ( 0 ) )
184- . ok ( ) ;
185- let ( count, last) : ( i64 , Option < i64 > ) = conn. query_row (
186- "SELECT COUNT(*), MAX(created_at) FROM settlements WHERE group_id = ?1" ,
187- params ! [ gid] ,
188- |r| Ok ( ( r. get ( 0 ) ?, r. get ( 1 ) ?) ) ,
189- ) ?;
190- out. push ( crate :: goal_retro:: GroupSettlementSnapshot {
191- group_id : * gid,
192- group_name : name. unwrap_or_else ( || format ! ( "组#{}" , gid) ) ,
193- settlement_count : count as usize ,
194- last_settled_at : last,
195- } ) ;
196- }
197- Ok ( out)
198- } )
199- }
200-
201- /// 复习统计(毕业报告口径:卡数/复习次数/90 天活跃日/低稳定性卡数——现算)。
202- pub fn goal_review_stats ( & self , goal_id : i64 , now_secs : i64 ) -> Result < crate :: goal_retro:: ReviewStats > {
203- let window_ms = ( now_secs - 90 * 86_400 ) * 1000 ;
204- self . with_conn ( |conn| {
205- let ( cards, logs, days90, weak) : ( i64 , i64 , i64 , i64 ) = conn. query_row (
206- "SELECT
207- (SELECT COUNT(*) FROM flashcards WHERE group_id IN
208- (SELECT group_id FROM goal_groups WHERE goal_id = ?1)),
209- (SELECT COUNT(*) FROM review_logs l JOIN flashcards c ON c.id = l.card_id
210- WHERE c.group_id IN (SELECT group_id FROM goal_groups WHERE goal_id = ?1)),
211- (SELECT COUNT(DISTINCT l.reviewed_at / 86400000) FROM review_logs l
212- JOIN flashcards c ON c.id = l.card_id
213- WHERE c.group_id IN (SELECT group_id FROM goal_groups WHERE goal_id = ?1)
214- AND l.reviewed_at >= ?2),
215- (SELECT COUNT(*) FROM flashcards WHERE group_id IN
216- (SELECT group_id FROM goal_groups WHERE goal_id = ?1)
217- AND json_valid(state_json) = 1
218- AND json_extract(state_json, '$.stability') < ?3)" ,
219- params ! [ goal_id, window_ms, crate :: goal_progress:: LOW_STABILITY_DAYS ] ,
220- |r| Ok ( ( r. get ( 0 ) ?, r. get ( 1 ) ?, r. get ( 2 ) ?, r. get ( 3 ) ?) ) ,
221- ) ?;
222- Ok ( crate :: goal_retro:: ReviewStats {
223- card_total : cards as usize ,
224- review_logs_total : logs as usize ,
225- review_days_90 : days90 as usize ,
226- weak_cards : weak as usize ,
227- } )
228- } )
229- }
230-
231- /// 成果物清单(组/笔记/卡/概念——「我留下了什么」;概念经体系引用跨链)。
232- pub fn goal_artifacts ( & self , goal_id : i64 ) -> Result < crate :: goal_retro:: ArtifactsInventory > {
233- self . with_conn ( |conn| {
234- let ( groups, notes, cards, concepts) : ( i64 , i64 , i64 , i64 ) = conn. query_row (
235- "SELECT
236- (SELECT COUNT(*) FROM goal_groups WHERE goal_id = ?1),
237- (SELECT COUNT(*) FROM notes WHERE group_id IN
238- (SELECT group_id FROM goal_groups WHERE goal_id = ?1)),
239- (SELECT COUNT(*) FROM flashcards WHERE group_id IN
240- (SELECT group_id FROM goal_groups WHERE goal_id = ?1)),
241- (SELECT COUNT(DISTINCT l.concept_id) FROM knowledge_links l
242- WHERE l.target_type = 'note_group'
243- AND l.target_id IN (SELECT group_id FROM goal_groups WHERE goal_id = ?1)
244- AND l.concept_id IS NOT NULL)" ,
245- params ! [ goal_id] ,
246- |r| Ok ( ( r. get ( 0 ) ?, r. get ( 1 ) ?, r. get ( 2 ) ?, r. get ( 3 ) ?) ) ,
247- ) ?;
248- Ok ( crate :: goal_retro:: ArtifactsInventory {
249- groups : groups as usize ,
250- notes : notes as usize ,
251- cards : cards as usize ,
252- concepts : concepts as usize ,
253- } )
254- } )
255- }
256173}
257174
258175#[ cfg( test) ]
0 commit comments