优化: 所有剩余UI/UX待办一批完成(持久化+AuditLog+解耦+total+原12大改+P2)
持久化(P1-c):新建 usePersistedRef composable,Tasks/AuditLog/ProjectDetail 等接入 localStorage
AuditLog(P1-d):后端 list_tool_executions 加 WHERE 筛选+返 {items,total,has_more},前端对接+长列折叠+筛选持久化
数据源解耦(P1-g):ProjectDetail projectTasks 按 project_id 独立加载 + ChatInput @项目联想独立加载(不读 store.tasks 当前页)
GitChanges(12a):后端 get_module_commits 加 git rev-list --count 返 total,前端显真实总数
原12大改:Dashboard 统计卡压底行(1)/Projects 列表卡片视图(2)/project_event 埋点排序(3)/TaskDetail 重设计(4)/IdeaDetail 重设计(5)/KnowledgeDetail 重设计(6)/界面持久化+侧栏Ctrl+B+审批数字键(7)/ProjectDetail 三栏改两栏(10)
P2打磨:文件浏览器(FileTree去重/FilePreview行号.md Diff/selectedFilePath归位)/settings反馈(假保存/端口校验)/AI会话(try-catch/scrollIntoView)/后端计数(move_queue事件/timeline total/workflow分页/import_batch分块)/杂项(TopBar/ConfirmDialog键盘/CIStatus i18n/ToolResultBody/ModuleNode/ApprovalDialog全选)
This commit is contained in:
@@ -72,6 +72,22 @@ fn build_order_clause(order_by: Option<&str>) -> Result<String> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 带「最近活跃时间」的项目记录(list_active_with_activity 专用返回结构)。
|
||||
///
|
||||
/// `last_active_at` = COALESCE(project_events 最新事件 created_at, projects.updated_at):
|
||||
/// 反映业务活跃(任务/灵感/状态推进等事件),无事件项目回退 updated_at。供前端「最近活跃排序」
|
||||
/// 展示,语义比 projects.updated_at(随元信息修改如改 description 也会刷新)更准确。
|
||||
///
|
||||
/// Serialize 供 IPC 层直接序列化回前端(serde 字段名 snake_case,对齐 ProjectRecord)。
|
||||
#[derive(Debug, Clone, serde::Serialize)]
|
||||
pub struct ProjectActivityRecord {
|
||||
/// 项目完整记录(嵌套序列化:record.id / record.name ...)
|
||||
#[serde(flatten)]
|
||||
pub record: ProjectRecord,
|
||||
/// 最近活跃时间(毫秒字符串)。前端独立消费,不混入 record 的 updated_at。
|
||||
pub last_active_at: String,
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// from_row 辅助函数
|
||||
// ============================================================
|
||||
@@ -204,6 +220,51 @@ impl ProjectRepo {
|
||||
.map_err(storage_err)?
|
||||
}
|
||||
|
||||
/// 列出未删除项目,**按「最近活跃」排序**(LEFT JOIN project_events 取最新事件时间,
|
||||
/// COALESCE 回退 updated_at)。
|
||||
///
|
||||
/// 业务语义:项目「最近活跃」应反映业务事件(任务/灵感/状态推进),而非 projects.updated_at
|
||||
/// (后者随元信息修改如改 description/path 也会刷新,不反映真实业务活跃)。本方法通过子查询
|
||||
/// 取每个项目 project_events 最新 created_at,无事件项目回退 updated_at,排序列统一可比。
|
||||
///
|
||||
/// 子查询而非 JOIN:project_events 每项目可能 0..N 条,JOIN 会展开需 DISTINCT,子查询
|
||||
/// `(SELECT MAX(created_at) FROM project_events WHERE project_id = projects.id)` 每行一次
|
||||
/// 聚合,语义清晰无笛卡尔积风险。单用户桌面应用项目数小,无性能压力。
|
||||
///
|
||||
/// 返回 `ProjectActivityRecord`(ProjectRecord + last_active_at 字段),供前端排序展示。
|
||||
pub async fn list_active_with_activity(&self) -> Result<Vec<ProjectActivityRecord>> {
|
||||
let conn = self.conn.clone();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let guard = conn.blocking_lock();
|
||||
// COALESCE(子查询最新事件, projects.updated_at):无事件项目回退 updated_at,
|
||||
// 保证所有项目有可比活跃时间。ORDER BY 该列 DESC,同时间 created_at DESC 兜底稳定。
|
||||
let mut stmt = guard
|
||||
.prepare(
|
||||
"SELECT id, name, description, status, idea_id, path, stack, created_at, updated_at, \
|
||||
COALESCE((SELECT MAX(created_at) FROM project_events WHERE project_id = projects.id), \
|
||||
updated_at) AS last_active_at \
|
||||
FROM projects WHERE deleted_at IS NULL \
|
||||
ORDER BY last_active_at DESC, created_at DESC",
|
||||
)
|
||||
.map_err(storage_err)?;
|
||||
let rows = stmt
|
||||
.query_map([], |row| {
|
||||
Ok(ProjectActivityRecord {
|
||||
record: project_from_row(row)?,
|
||||
last_active_at: row.get("last_active_at")?,
|
||||
})
|
||||
})
|
||||
.map_err(storage_err)?;
|
||||
let mut results = Vec::new();
|
||||
for r in rows {
|
||||
results.push(r.map_err(storage_err)?);
|
||||
}
|
||||
Ok(results)
|
||||
})
|
||||
.await
|
||||
.map_err(storage_err)?
|
||||
}
|
||||
|
||||
/// 按条件查询未删除项目(P2/P3:关键词搜索 + 排序 + 分页)。
|
||||
///
|
||||
/// 复用 `KnowledgeRepo::search` 动态 WHERE 拼接模式:按可选字段 if-let 拼 SQL 子句 +
|
||||
|
||||
Reference in New Issue
Block a user