Skip to content

Commit 5b9a4b5

Browse files
committed
refactor(db_goals): 拆绑定域至 db_goals_binding.rs
1 parent 6d4787d commit 5b9a4b5

2 files changed

Lines changed: 70 additions & 52 deletions

File tree

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

Lines changed: 3 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use rusqlite::{params, Connection};
1212

1313
use crate::db::{unix_seconds, Db};
1414
use crate::error::Result;
15-
use crate::goal_schema::{CRITERIA_GROUP_SETTLED, MILESTONE_DONE, MILESTONE_IN_PROGRESS, MILESTONE_PENDING};
1615

1716
// 子模块声明(AGENTS.md §3 单文件 ≤300 行;#[path] 使兄弟文件平铺在同目录,各自带 @ai-context)。
1817
/// 里程碑域:里程碑 CRUD + 三写路径共用的插入行 helper。
@@ -27,6 +26,9 @@ mod goal;
2726
/// 回顾域:结算快照 / 复习统计 / 成果物清单聚合取数。
2827
#[path = "db_goals_retro.rs"]
2928
mod retro;
29+
/// 绑定域:目标↔组绑定 + 组结算钩子。
30+
#[path = "db_goals_binding.rs"]
31+
mod binding;
3032

3133
/// 三表 DDL + 索引(幂等:CREATE TABLE IF NOT EXISTS;旧库升级自动补表)。
3234
pub(crate) fn init(conn: &Connection) -> Result<()> {
@@ -81,57 +83,6 @@ pub(crate) fn init(conn: &Connection) -> Result<()> {
8183

8284
impl Db {
8385

84-
/// 绑定组到目标(UNIQUE 幂等:重复绑定返回 false;组不存在靠外键报错,
85-
/// 命令层先行校验)。
86-
pub fn bind_group(&self, goal_id: i64, group_id: i64) -> Result<bool> {
87-
self.with_conn(|conn| {
88-
let affected = conn.execute(
89-
"INSERT OR IGNORE INTO goal_groups (goal_id, group_id, added_at) VALUES (?1, ?2, ?3)",
90-
params![goal_id, group_id, unix_seconds()],
91-
)?;
92-
Ok(affected > 0)
93-
})
94-
}
95-
96-
/// 解绑组(不影响组本身:组是唯一容器,绑定只是管道)。
97-
pub fn unbind_group(&self, goal_id: i64, group_id: i64) -> Result<bool> {
98-
self.with_conn(|conn| {
99-
let affected = conn.execute(
100-
"DELETE FROM goal_groups WHERE goal_id = ?1 AND group_id = ?2",
101-
params![goal_id, group_id],
102-
)?;
103-
Ok(affected > 0)
104-
})
105-
}
106-
107-
/// 目标绑定组 id 列表(进度聚合/详情视图数据源)。
108-
pub fn list_goal_group_ids(&self, goal_id: i64) -> Result<Vec<i64>> {
109-
self.with_conn(|conn| {
110-
let mut stmt =
111-
conn.prepare("SELECT group_id FROM goal_groups WHERE goal_id = ?1 ORDER BY id ASC")?;
112-
let rows = stmt.query_map(params![goal_id], |r| r.get::<_, i64>(0))?;
113-
rows.collect::<rusqlite::Result<Vec<_>>>().map_err(Into::into)
114-
})
115-
}
116-
117-
/// 组结算钩子(execute_settlement 后调用):绑该组的 group_settled
118-
/// 里程碑 pending/in_progress → done(自动通过,进度信号随之生效)。
119-
pub fn mark_group_settled_milestones(&self, group_id: i64) -> Result<usize> {
120-
let now = unix_seconds();
121-
self.with_conn(|conn| {
122-
let affected = conn.execute(
123-
&format!(
124-
"UPDATE goal_milestones SET status = '{}', completed_at = ?1
125-
WHERE criteria_type = '{}' AND ref_group_id = ?2
126-
AND status IN ('{}', '{}')",
127-
MILESTONE_DONE, CRITERIA_GROUP_SETTLED, MILESTONE_PENDING, MILESTONE_IN_PROGRESS
128-
),
129-
params![now, group_id],
130-
)?;
131-
Ok(affected)
132-
})
133-
}
134-
13586
// ─────────────────────── v0.18.1 毕业报告(REQ-255/256) ───────────────────────
13687

13788
/// 写毕业报告快照(目标删除后保留;goal_id SET NULL——报告独立于 goals 行)。
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//! 绑定域:目标↔组绑定(goal_groups)+ 组结算钩子。
2+
//!
3+
//! @ai-context: 由 db_goals.rs 按域拆出(AGENTS.md §3 单文件 ≤300 行)。
4+
//! mark_group_settled_milestones 由结算链路(commands_settlement.rs)
5+
//! 在结算事务**之外**独立取锁调用——不得把它并进任何事务。
6+
//! @ai-context: goal_groups 的 UNIQUE(goal_id, group_id) 是绑定幂等语义来源
7+
//! (DDL 在主文件 db_goals.rs 的 init);另两条插入路径在
8+
//! db_goals_goal.rs(create_goal)与 db_goals_plan.rs(apply_plan_core)。
9+
10+
use rusqlite::params;
11+
12+
use crate::db::{unix_seconds, Db};
13+
use crate::error::Result;
14+
use crate::goal_schema::{CRITERIA_GROUP_SETTLED, MILESTONE_DONE, MILESTONE_IN_PROGRESS, MILESTONE_PENDING};
15+
16+
impl Db {
17+
/// 绑定组到目标(UNIQUE 幂等:重复绑定返回 false;组不存在靠外键报错,
18+
/// 命令层先行校验)。
19+
pub fn bind_group(&self, goal_id: i64, group_id: i64) -> Result<bool> {
20+
self.with_conn(|conn| {
21+
let affected = conn.execute(
22+
"INSERT OR IGNORE INTO goal_groups (goal_id, group_id, added_at) VALUES (?1, ?2, ?3)",
23+
params![goal_id, group_id, unix_seconds()],
24+
)?;
25+
Ok(affected > 0)
26+
})
27+
}
28+
29+
/// 解绑组(不影响组本身:组是唯一容器,绑定只是管道)。
30+
pub fn unbind_group(&self, goal_id: i64, group_id: i64) -> Result<bool> {
31+
self.with_conn(|conn| {
32+
let affected = conn.execute(
33+
"DELETE FROM goal_groups WHERE goal_id = ?1 AND group_id = ?2",
34+
params![goal_id, group_id],
35+
)?;
36+
Ok(affected > 0)
37+
})
38+
}
39+
40+
/// 目标绑定组 id 列表(进度聚合/详情视图数据源)。
41+
pub fn list_goal_group_ids(&self, goal_id: i64) -> Result<Vec<i64>> {
42+
self.with_conn(|conn| {
43+
let mut stmt =
44+
conn.prepare("SELECT group_id FROM goal_groups WHERE goal_id = ?1 ORDER BY id ASC")?;
45+
let rows = stmt.query_map(params![goal_id], |r| r.get::<_, i64>(0))?;
46+
rows.collect::<rusqlite::Result<Vec<_>>>().map_err(Into::into)
47+
})
48+
}
49+
50+
/// 组结算钩子(execute_settlement 后调用):绑该组的 group_settled
51+
/// 里程碑 pending/in_progress → done(自动通过,进度信号随之生效)。
52+
pub fn mark_group_settled_milestones(&self, group_id: i64) -> Result<usize> {
53+
let now = unix_seconds();
54+
self.with_conn(|conn| {
55+
let affected = conn.execute(
56+
&format!(
57+
"UPDATE goal_milestones SET status = '{}', completed_at = ?1
58+
WHERE criteria_type = '{}' AND ref_group_id = ?2
59+
AND status IN ('{}', '{}')",
60+
MILESTONE_DONE, CRITERIA_GROUP_SETTLED, MILESTONE_PENDING, MILESTONE_IN_PROGRESS
61+
),
62+
params![now, group_id],
63+
)?;
64+
Ok(affected)
65+
})
66+
}
67+
}

0 commit comments

Comments
 (0)