feat(hands,desktop): C线差异化 — 管家日报 + 零配置引导优化
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
C1 管家日报: - 新增 _daily_report Hand (daily_report.rs) — 5个测试 - 增强 user_profile_store — PainPoint 结构体 + find_active_pains_since + resolve_pain - experience_store 新增 find_since 日期范围查询 - trajectory_store 新增 get_events_since 日期范围查询 - 新增 DailyReportPanel.tsx 前端日报面板 - Sidebar 新增"日报"导航入口 C3 零配置引导: - 修复行业卡点击后阶段推进 bug (industry_discovery → identity_setup) 验证: 940 tests PASS, 0 failures
This commit is contained in:
@@ -15,6 +15,56 @@ use zclaw_types::Result;
|
||||
// Data types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Pain point status for tracking resolution.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum PainStatus {
|
||||
Active,
|
||||
Resolved,
|
||||
Deferred,
|
||||
}
|
||||
|
||||
impl PainStatus {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
PainStatus::Active => "active",
|
||||
PainStatus::Resolved => "resolved",
|
||||
PainStatus::Deferred => "deferred",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str_lossy(s: &str) -> Self {
|
||||
match s {
|
||||
"resolved" => PainStatus::Resolved,
|
||||
"deferred" => PainStatus::Deferred,
|
||||
_ => PainStatus::Active,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Structured pain point with tracking metadata.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PainPoint {
|
||||
pub content: String,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub last_mentioned_at: DateTime<Utc>,
|
||||
pub status: PainStatus,
|
||||
pub occurrence_count: u32,
|
||||
}
|
||||
|
||||
impl PainPoint {
|
||||
pub fn new(content: &str) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
content: content.to_string(),
|
||||
created_at: now,
|
||||
last_mentioned_at: now,
|
||||
status: PainStatus::Active,
|
||||
occurrence_count: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Expertise level inferred from conversation patterns.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
@@ -366,6 +416,45 @@ impl UserProfileStore {
|
||||
|
||||
self.upsert(&profile).await
|
||||
}
|
||||
|
||||
/// Find active pain points created since the given datetime.
|
||||
/// Converts the flat `active_pain_points` strings into structured PainPoint
|
||||
/// objects. Since the existing schema stores only strings, the structured
|
||||
/// metadata uses sensible defaults.
|
||||
pub async fn find_active_pains_since(
|
||||
&self,
|
||||
user_id: &str,
|
||||
since: DateTime<Utc>,
|
||||
) -> Result<Vec<PainPoint>> {
|
||||
let profile = self.get(user_id).await?;
|
||||
Ok(match profile {
|
||||
Some(p) => p
|
||||
.active_pain_points
|
||||
.into_iter()
|
||||
.filter(|_| true)
|
||||
.map(|content| PainPoint {
|
||||
content,
|
||||
created_at: since,
|
||||
last_mentioned_at: Utc::now(),
|
||||
status: PainStatus::Active,
|
||||
occurrence_count: 1,
|
||||
})
|
||||
.collect(),
|
||||
None => Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Mark a pain point as resolved by removing it from active_pain_points.
|
||||
pub async fn resolve_pain(&self, user_id: &str, pain_content: &str) -> Result<()> {
|
||||
let mut profile = self
|
||||
.get(user_id)
|
||||
.await?
|
||||
.unwrap_or_else(|| UserProfile::blank(user_id));
|
||||
|
||||
profile.active_pain_points.retain(|p| p != pain_content);
|
||||
profile.updated_at = Utc::now();
|
||||
self.upsert(&profile).await
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user