@@ -12,11 +12,7 @@ use rusqlite::{params, Connection};
1212
1313use crate :: db:: { unix_seconds, Db } ;
1414use crate :: error:: Result ;
15- use crate :: goal_schema:: {
16- Goal , NewGoal , CRITERIA_GROUP_SETTLED , MILESTONE_DONE ,
17- MILESTONE_IN_PROGRESS , MILESTONE_PENDING ,
18- } ;
19- use self :: milestone:: add_milestone_row;
15+ use crate :: goal_schema:: { CRITERIA_GROUP_SETTLED , MILESTONE_DONE , MILESTONE_IN_PROGRESS , MILESTONE_PENDING } ;
2016
2117// 子模块声明(AGENTS.md §3 单文件 ≤300 行;#[path] 使兄弟文件平铺在同目录,各自带 @ai-context)。
2218/// 里程碑域:里程碑 CRUD + 三写路径共用的插入行 helper。
@@ -25,6 +21,9 @@ mod milestone;
2521/// AI 规划域:规划落库事务 / 体系链接 / 概念活动 / 规划上下文。
2622#[ path = "db_goals_plan.rs" ]
2723mod plan;
24+ /// 目标域:goals 实体 CRUD(含事务建目标)+ goals 行映射。
25+ #[ path = "db_goals_goal.rs" ]
26+ mod goal;
2827
2928/// 三表 DDL + 索引(幂等:CREATE TABLE IF NOT EXISTS;旧库升级自动补表)。
3029pub ( crate ) fn init ( conn : & Connection ) -> Result < ( ) > {
@@ -78,113 +77,6 @@ pub(crate) fn init(conn: &Connection) -> Result<()> {
7877}
7978
8079impl Db {
81- /// 新建目标(事务:goal + 里程碑草案 + 初始绑定组;status=active 一步到位)。
82- pub fn create_goal ( & self , new : & NewGoal ) -> Result < Goal > {
83- let now = unix_seconds ( ) ;
84- self . with_conn ( |conn| {
85- let tx = conn. unchecked_transaction ( ) ?;
86- tx. execute (
87- "INSERT INTO goals (name, domain_tag, status, horizon_end, success_criteria_json, intent_json, created_at, updated_at)
88- VALUES (?1, ?2, 'active', ?3, ?4, ?5, ?6, ?6)" ,
89- params ! [
90- new. name,
91- new. domain_tag,
92- new. horizon_end,
93- new. success_criteria_json,
94- new. intent_json,
95- now
96- ] ,
97- ) ?;
98- let goal_id = tx. last_insert_rowid ( ) ;
99- for ( idx, m) in new. milestones . iter ( ) . enumerate ( ) {
100- add_milestone_row ( & tx, goal_id, m, idx as i64 , now) ?;
101- }
102- for gid in & new. group_ids {
103- tx. execute (
104- "INSERT OR IGNORE INTO goal_groups (goal_id, group_id, added_at) VALUES (?1, ?2, ?3)" ,
105- params ! [ goal_id, gid, now] ,
106- ) ?;
107- }
108- tx. commit ( ) ?;
109- Ok ( Goal {
110- id : goal_id,
111- name : new. name . clone ( ) ,
112- domain_tag : new. domain_tag . clone ( ) ,
113- status : "active" . to_string ( ) ,
114- horizon_end : new. horizon_end ,
115- success_criteria_json : new. success_criteria_json . clone ( ) ,
116- intent_json : new. intent_json . clone ( ) ,
117- created_at : now,
118- completed_at : None ,
119- updated_at : now,
120- } )
121- } )
122- }
123-
124- /// 全部目标(列表页;按创建时间倒序——同秒并列时按 id 倒序,顺序确定)。
125- pub fn list_goals ( & self ) -> Result < Vec < Goal > > {
126- self . with_conn ( |conn| {
127- let mut stmt =
128- conn. prepare ( "SELECT * FROM goals ORDER BY created_at DESC, id DESC" ) ?;
129- let rows = stmt. query_map ( [ ] , row_to_goal) ?;
130- rows. collect :: < rusqlite:: Result < Vec < _ > > > ( ) . map_err ( Into :: into)
131- } )
132- }
133-
134- /// 按 id 读取目标(不存在 → None)。
135- pub fn get_goal ( & self , id : i64 ) -> Result < Option < Goal > > {
136- self . with_conn ( |conn| {
137- let mut stmt = conn. prepare ( "SELECT * FROM goals WHERE id = ?1" ) ?;
138- let mut rows = stmt. query_map ( params ! [ id] , row_to_goal) ?;
139- match rows. next ( ) {
140- Some ( Ok ( g) ) => Ok ( Some ( g) ) ,
141- Some ( Err ( e) ) => Err ( e. into ( ) ) ,
142- None => Ok ( None ) ,
143- }
144- } )
145- }
146-
147- /// 更新目标元数据(名称/领域/时限/判据配方/访谈答案——重访谈配方重推入口)。
148- pub fn update_goal_core (
149- & self ,
150- id : i64 ,
151- name : & str ,
152- domain_tag : Option < & str > ,
153- horizon_end : Option < i64 > ,
154- success_criteria_json : & str ,
155- intent_json : & str ,
156- ) -> Result < bool > {
157- self . with_conn ( |conn| {
158- let affected = conn. execute (
159- "UPDATE goals SET name = ?2, domain_tag = ?3, horizon_end = ?4,
160- success_criteria_json = ?5, intent_json = ?6, updated_at = ?7
161- WHERE id = ?1" ,
162- params ! [ id, name, domain_tag, horizon_end, success_criteria_json, intent_json, unix_seconds( ) ] ,
163- ) ?;
164- Ok ( affected > 0 )
165- } )
166- }
167-
168- /// 状态转移(graduated/abandoned 时写 completed_at;恢复后清空)。
169- pub fn set_goal_status ( & self , id : i64 , status : & str ) -> Result < bool > {
170- let now = unix_seconds ( ) ;
171- self . with_conn ( |conn| {
172- let affected = conn. execute (
173- "UPDATE goals SET status = ?2, completed_at = CASE WHEN ?3 THEN ?4 ELSE NULL END, updated_at = ?4
174- WHERE id = ?1" ,
175- params ! [ id, status, status == "graduated" || status == "abandoned" , now] ,
176- ) ?;
177- Ok ( affected > 0 )
178- } )
179- }
180-
181- /// 删除目标(里程碑/绑定随 FK CASCADE;毕业快照保留属 M2——M1 无快照表)。
182- pub fn delete_goal ( & self , id : i64 ) -> Result < bool > {
183- self . with_conn ( |conn| {
184- let affected = conn. execute ( "DELETE FROM goals WHERE id = ?1" , params ! [ id] ) ?;
185- Ok ( affected > 0 )
186- } )
187- }
18880
18981 /// 绑定组到目标(UNIQUE 幂等:重复绑定返回 false;组不存在靠外键报错,
19082 /// 命令层先行校验)。
@@ -363,22 +255,6 @@ impl Db {
363255 }
364256}
365257
366- /// goals 行 → Goal。
367- fn row_to_goal ( row : & rusqlite:: Row < ' _ > ) -> rusqlite:: Result < Goal > {
368- Ok ( Goal {
369- id : row. get ( 0 ) ?,
370- name : row. get ( 1 ) ?,
371- domain_tag : row. get ( 2 ) ?,
372- status : row. get ( 3 ) ?,
373- horizon_end : row. get ( 4 ) ?,
374- success_criteria_json : row. get ( 5 ) ?,
375- intent_json : row. get ( 6 ) ?,
376- created_at : row. get ( 7 ) ?,
377- completed_at : row. get ( 8 ) ?,
378- updated_at : row. get ( 9 ) ?,
379- } )
380- }
381-
382258#[ cfg( test) ]
383259#[ path = "db_goals_tests.rs" ]
384260mod tests;
0 commit comments