Skip to content

Commit 67427ee

Browse files
committed
refactor(rust): 拆 commands_goals 里程碑写入域(501→344)
1 parent df2b1a5 commit 67427ee

6 files changed

Lines changed: 202 additions & 174 deletions

File tree

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,13 @@ pub fn handle() -> impl Fn(tauri::ipc::Invoke<tauri::Wry>) -> bool + Send + Sync
133133
crate::commands_goals::update_goal_interview,
134134
crate::commands_goals::delete_goal,
135135
crate::commands_goals::update_goal_status,
136-
crate::commands_goals::add_goal_milestone,
137-
crate::commands_goals::update_goal_milestone,
138-
crate::commands_goals::delete_goal_milestone,
139-
crate::commands_goals::set_goal_milestone_status,
140-
crate::commands_goals::bind_goal_group,
141-
crate::commands_goals::unbind_goal_group,
142-
crate::commands_goals::suggest_goal_milestones,
136+
crate::commands_goals::milestones::add_goal_milestone,
137+
crate::commands_goals::milestones::update_goal_milestone,
138+
crate::commands_goals::milestones::delete_goal_milestone,
139+
crate::commands_goals::milestones::set_goal_milestone_status,
140+
crate::commands_goals::milestones::bind_goal_group,
141+
crate::commands_goals::milestones::unbind_goal_group,
142+
crate::commands_goals::milestones::suggest_goal_milestones,
143143
// v0.18.1(REQ-255~257):毕业仪式/回顾流/放弃/毕业档案
144144
crate::commands_goals_lifecycle::goal_settle,
145145
crate::commands_goals_lifecycle::goal_retro,

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

Lines changed: 6 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ use crate::db::{unix_seconds, Db};
1717
use crate::goal_interview::{derive_criteria, horizon_end_secs};
1818
use crate::goal_progress::GoalSignals;
1919
use crate::goal_schema::{
20-
Goal, GoalIntent, GoalMilestone, NewGoal, NewMilestone, SuccessCriteria, CRITERIA_GROUP_SETTLED,
21-
CRITERIA_MANUAL, MILESTONE_DONE, MILESTONE_IN_PROGRESS, MILESTONE_PENDING, MILESTONE_SKIPPED,
22-
TIER_DEFAULT,
20+
Goal, GoalIntent, NewGoal, NewMilestone, SuccessCriteria, CRITERIA_MANUAL, TIER_DEFAULT,
2321
};
2422
use crate::video_profile_domain::DomainKind;
2523

@@ -28,6 +26,11 @@ use crate::video_profile_domain::DomainKind;
2826
#[path = "commands_goals_views.rs"]
2927
pub(crate) mod views;
3028

29+
/// 里程碑与目标↔组绑定写入域(7 条命令 + 4 个 inner + 2 个白名单常量;6 emit)。
30+
/// `pub(crate)`:同 `views`(注册路径三段式)且测试文件按 `commands_goals::milestones::x` 导入。
31+
#[path = "commands_goals_milestones.rs"]
32+
pub(crate) mod milestones;
33+
3134
/// 一周秒数(草案 due_at 换算:第 N 周 = created_at + N*7d)。
3235
const WEEK_SECS: i64 = 7 * 86_400;
3336
/// 访谈答案文本上限(防御超大 payload;chips/填空兜底的答案均为短文本)。
@@ -140,105 +143,8 @@ pub fn update_goal_status(state: State<'_, AppState>, id: i64, status: String) -
140143
Ok(ok)
141144
}
142145

143-
/// 里程碑草案建议(宣言页预填;前端薄——单一事实源在 goal_interview.rs)。
144-
#[tauri::command]
145-
pub fn suggest_goal_milestones(
146-
_state: State<'_, AppState>,
147-
level: Option<String>,
148-
weekly_commitment: Option<String>,
149-
) -> Vec<crate::goal_schema::MilestoneDraft> {
150-
crate::goal_interview::suggest_milestones(level.as_deref(), weekly_commitment.as_deref())
151-
}
152-
153-
/// 里程碑增删改与状态流转(status → done 记 goal_milestone_done)。
154-
#[tauri::command]
155-
pub fn add_goal_milestone(
156-
state: State<'_, AppState>,
157-
goal_id: i64,
158-
title: String,
159-
due_at: Option<i64>,
160-
criteria_type: Option<String>,
161-
ref_group_id: Option<i64>,
162-
) -> Result<GoalMilestone, String> {
163-
let m = add_goal_milestone_inner(&state.db, goal_id, &title, due_at, criteria_type, ref_group_id)?;
164-
// REQ-278:里程碑新增 → 广播 goals 域
165-
crate::notify::emit_changed(&state.app, crate::notify::DataDomain::Goals);
166-
Ok(m)
167-
}
168-
169-
#[tauri::command]
170-
pub fn update_goal_milestone(
171-
state: State<'_, AppState>,
172-
id: i64,
173-
title: String,
174-
due_at: Option<i64>,
175-
) -> Result<bool, String> {
176-
if id <= 0 {
177-
return Err("无效的里程碑 id".to_string());
178-
}
179-
let title = normalize_title(title, "未命名里程碑");
180-
let ok = state.db.update_milestone(id, &title, due_at).map_err(|e| e.to_string())?;
181-
// REQ-278:里程碑更新 → 广播 goals 域
182-
if ok {
183-
crate::notify::emit_changed(&state.app, crate::notify::DataDomain::Goals);
184-
}
185-
Ok(ok)
186-
}
187-
188-
#[tauri::command]
189-
pub fn delete_goal_milestone(state: State<'_, AppState>, id: i64) -> Result<bool, String> {
190-
if id <= 0 {
191-
return Err("无效的里程碑 id".to_string());
192-
}
193-
let ok = state.db.delete_milestone(id).map_err(|e| e.to_string())?;
194-
// REQ-278:里程碑删除 → 广播 goals 域
195-
if ok {
196-
crate::notify::emit_changed(&state.app, crate::notify::DataDomain::Goals);
197-
}
198-
Ok(ok)
199-
}
200-
201-
#[tauri::command]
202-
pub fn set_goal_milestone_status(state: State<'_, AppState>, id: i64, status: String) -> Result<bool, String> {
203-
let ok = set_goal_milestone_status_inner(&state.db, id, &status)?;
204-
// REQ-278:里程碑状态流转 → 广播 goals 域
205-
if ok {
206-
crate::notify::emit_changed(&state.app, crate::notify::DataDomain::Goals);
207-
}
208-
Ok(ok)
209-
}
210-
211-
/// 绑定/解绑组(N:M——一组可服务多目标;组仍是唯一容器)。
212-
#[tauri::command]
213-
pub fn bind_goal_group(state: State<'_, AppState>, goal_id: i64, group_id: i64) -> Result<bool, String> {
214-
let ok = bind_goal_group_inner(&state.db, goal_id, group_id)?;
215-
// REQ-278:绑组(N:M)→ 广播 goals 域(组侧服务标随目标页刷新)
216-
if ok {
217-
crate::notify::emit_changed(&state.app, crate::notify::DataDomain::Goals);
218-
}
219-
Ok(ok)
220-
}
221-
222-
#[tauri::command]
223-
pub fn unbind_goal_group(state: State<'_, AppState>, goal_id: i64, group_id: i64) -> Result<bool, String> {
224-
let ok = unbind_goal_group_inner(&state.db, goal_id, group_id)?;
225-
// REQ-278:解绑组 → 广播 goals 域
226-
if ok {
227-
crate::notify::emit_changed(&state.app, crate::notify::DataDomain::Goals);
228-
}
229-
Ok(ok)
230-
}
231-
232146
// ─────────────────────────── inner(供测试与复用) ───────────────────────────
233147

234-
/// 里程碑状态白名单(TEXT 无 CHECK 惯例——命令层白名单先例)。
235-
const MILESTONE_STATUSES: [&str; 4] =
236-
[MILESTONE_PENDING, MILESTONE_IN_PROGRESS, MILESTONE_DONE, MILESTONE_SKIPPED];
237-
238-
/// 里程碑判据类型白名单(self_test 仅登记占位契约——CRITERIA_SELF_TEST 常量
239-
/// 供 M3 真实化后启用;本版不写入)。
240-
const CRITERIA_TYPES: [&str; 2] = [CRITERIA_MANUAL, CRITERIA_GROUP_SETTLED];
241-
242148
/// 校验目标存在(id 合法 + 行存在)。
243149
pub(crate) fn require_goal(db: &Db, id: i64) -> Result<(), String> {
244150
if id <= 0 {
@@ -389,69 +295,6 @@ pub(crate) fn update_goal_status_inner(db: &Db, id: i64, status: &str) -> Result
389295
db.set_goal_status(id, status).map_err(|e| e.to_string())
390296
}
391297

392-
pub(crate) fn add_goal_milestone_inner(
393-
db: &Db,
394-
goal_id: i64,
395-
title: &str,
396-
due_at: Option<i64>,
397-
criteria_type: Option<String>,
398-
ref_group_id: Option<i64>,
399-
) -> Result<GoalMilestone, String> {
400-
require_goal(db, goal_id)?;
401-
let criteria_type = criteria_type.unwrap_or_else(|| CRITERIA_MANUAL.to_string());
402-
if !CRITERIA_TYPES.contains(&criteria_type.as_str()) {
403-
return Err(format!("不支持的里程碑判据类型: {}(支持: {})", criteria_type, CRITERIA_TYPES.join("/")));
404-
}
405-
if criteria_type == CRITERIA_GROUP_SETTLED {
406-
let gid = ref_group_id.ok_or_else(|| "group_settled 型里程碑必须绑定组".to_string())?;
407-
if db.get_group(gid).map_err(|e| e.to_string())?.is_none() {
408-
return Err(format!("笔记组不存在: {}", gid));
409-
}
410-
}
411-
db.add_milestone(goal_id, &NewMilestone {
412-
title: normalize_title(title.to_string(), "未命名里程碑"),
413-
due_at,
414-
order_idx: 0,
415-
criteria_type,
416-
ref_group_id,
417-
})
418-
.map_err(|e| e.to_string())
419-
}
420-
421-
pub(crate) fn set_goal_milestone_status_inner(db: &Db, id: i64, status: &str) -> Result<bool, String> {
422-
if !MILESTONE_STATUSES.contains(&status) {
423-
return Err(format!("不支持的里程碑状态: {}(支持: {})", status, MILESTONE_STATUSES.join("/")));
424-
}
425-
// 取旧状态(幂等埋点判据:仅「未完成 → 完成」的转变记 goal_milestone_done)
426-
let prev = db
427-
.get_milestone(id)
428-
.map_err(|e| e.to_string())?
429-
.ok_or_else(|| format!("里程碑不存在: {}", id))?;
430-
let was_done = prev.status == MILESTONE_DONE;
431-
let ok = db.set_milestone_status(id, status).map_err(|e| e.to_string())?;
432-
if !ok {
433-
return Err(format!("里程碑不存在: {}", id));
434-
}
435-
if !was_done && status == MILESTONE_DONE {
436-
let payload = serde_json::json!({ "milestoneId": id }).to_string();
437-
let _ = db.add_metric_event("goal_milestone_done", &payload);
438-
}
439-
Ok(true)
440-
}
441-
442-
pub(crate) fn bind_goal_group_inner(db: &Db, goal_id: i64, group_id: i64) -> Result<bool, String> {
443-
require_goal(db, goal_id)?;
444-
if db.get_group(group_id).map_err(|e| e.to_string())?.is_none() {
445-
return Err(format!("笔记组不存在: {}", group_id));
446-
}
447-
db.bind_group(goal_id, group_id).map_err(|e| e.to_string())
448-
}
449-
450-
pub(crate) fn unbind_goal_group_inner(db: &Db, goal_id: i64, group_id: i64) -> Result<bool, String> {
451-
require_goal(db, goal_id)?;
452-
db.unbind_group(goal_id, group_id).map_err(|e| e.to_string())
453-
}
454-
455298
/// 领域标签校验(与 commands_groups 同口径:kebab-case 白名单;空 → None)。
456299
fn parse_domain(domain_tag: Option<&str>) -> Result<Option<String>, String> {
457300
match domain_tag {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! commands_goals_lifecycle 单测(毕业仪式守卫矩阵/回顾流/放弃/档案——内存库)。
22
3-
use crate::commands_goals::{create_goal_inner, set_goal_milestone_status_inner, GoalCreateInput, GoalMilestoneInput};
3+
use crate::commands_goals::milestones::set_goal_milestone_status_inner;
4+
use crate::commands_goals::{create_goal_inner, GoalCreateInput, GoalMilestoneInput};
45
use crate::commands_goals_lifecycle::{
56
goal_abandon_inner, goal_retro_inner, goal_settle_inner, list_goal_graduations_inner,
67
};

0 commit comments

Comments
 (0)