新增: AI Chat多项增强(审批去重/编辑重发/导出/实体引用/会话置顶搜索)+任务推进链df-nodes落地
This commit is contained in:
@@ -1047,6 +1047,7 @@ fn ai_conversation_from_row(row: &Row<'_>) -> std::result::Result<AiConversation
|
||||
model: row.get("model")?,
|
||||
models: row.get("models")?,
|
||||
archived: row.get::<_, i32>("archived")? != 0,
|
||||
pinned: row.get::<_, i32>("pinned")? != 0,
|
||||
prompt_tokens: row.get("prompt_tokens")?,
|
||||
completion_tokens: row.get("completion_tokens")?,
|
||||
created_at: row.get("created_at")?,
|
||||
@@ -1137,10 +1138,11 @@ impl_repo!(
|
||||
from_row => |row| ai_conversation_from_row(row),
|
||||
insert => |conn, rec| {
|
||||
conn.execute(
|
||||
"INSERT INTO ai_conversations (id, title, messages, provider_id, model, models, archived, prompt_tokens, completion_tokens, created_at, updated_at)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)",
|
||||
"INSERT INTO ai_conversations (id, title, messages, provider_id, model, models, archived, pinned, prompt_tokens, completion_tokens, created_at, updated_at)
|
||||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)",
|
||||
params![
|
||||
rec.id, rec.title, rec.messages, rec.provider_id, rec.model, rec.models, rec.archived,
|
||||
if rec.pinned { 1i32 } else { 0i32 },
|
||||
rec.prompt_tokens, rec.completion_tokens,
|
||||
rec.created_at, rec.updated_at
|
||||
],
|
||||
@@ -1148,9 +1150,10 @@ impl_repo!(
|
||||
},
|
||||
update => |conn, rec| {
|
||||
conn.execute(
|
||||
"UPDATE ai_conversations SET title = ?1, messages = ?2, provider_id = ?3, model = ?4, models = ?5, archived = ?6, prompt_tokens = ?7, completion_tokens = ?8, updated_at = ?9 WHERE id = ?10",
|
||||
"UPDATE ai_conversations SET title = ?1, messages = ?2, provider_id = ?3, model = ?4, models = ?5, archived = ?6, pinned = ?7, prompt_tokens = ?8, completion_tokens = ?9, updated_at = ?10 WHERE id = ?11",
|
||||
params![
|
||||
rec.title, rec.messages, rec.provider_id, rec.model, rec.models, rec.archived,
|
||||
if rec.pinned { 1i32 } else { 0i32 },
|
||||
rec.prompt_tokens, rec.completion_tokens,
|
||||
rec.updated_at, rec.id
|
||||
],
|
||||
@@ -1237,6 +1240,42 @@ impl AiToolExecutionRepo {
|
||||
.await
|
||||
.map_err(storage_err)?
|
||||
}
|
||||
|
||||
/// 审批历史面板分页查询:按 requested_at 倒序(最新在前),limit 默认 50。
|
||||
///
|
||||
/// 与 list_pending 同理走专用 SELECT,绕过通用 query 宏(后者硬编码
|
||||
/// ORDER BY created_at,本表无该列)。limit/offset 上限钳制(limit ≤ 200),
|
||||
/// 防前端恶意/失误传超大值。
|
||||
pub async fn list_recent(
|
||||
&self,
|
||||
limit: u32,
|
||||
offset: u32,
|
||||
) -> Result<Vec<AiToolExecutionRecord>> {
|
||||
let conn = self.conn.clone();
|
||||
// 钳制 limit 防滥用(默认 50,最大 200)
|
||||
let safe_limit = limit.min(200) as i64;
|
||||
let safe_offset = offset as i64;
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let guard = conn.blocking_lock();
|
||||
let mut stmt = guard
|
||||
.prepare(
|
||||
"SELECT * FROM ai_tool_executions ORDER BY requested_at DESC LIMIT ?1 OFFSET ?2",
|
||||
)
|
||||
.map_err(storage_err)?;
|
||||
let rows = stmt
|
||||
.query_map(params![safe_limit, safe_offset], |row| {
|
||||
ai_tool_execution_from_row(row)
|
||||
})
|
||||
.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)?
|
||||
}
|
||||
}
|
||||
|
||||
impl_repo!(
|
||||
@@ -1611,6 +1650,26 @@ impl AiConversationRepo {
|
||||
.await
|
||||
.map_err(storage_err)?
|
||||
}
|
||||
|
||||
/// 设置置顶标记(仅改 pinned,不动 updated_at) — UX-17
|
||||
///
|
||||
/// 同 set_archived:置顶是纯元数据标记,不应改变相对时间。前端排序读 pinned DESC, updated_at DESC。
|
||||
pub async fn set_pinned(&self, id: &str, pinned: bool) -> Result<bool> {
|
||||
let conn = self.conn.clone();
|
||||
let id = id.to_owned();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let guard = conn.blocking_lock();
|
||||
let affected = guard
|
||||
.execute(
|
||||
"UPDATE ai_conversations SET pinned = ?1 WHERE id = ?2",
|
||||
params![if pinned { 1 } else { 0 }, id],
|
||||
)
|
||||
.map_err(storage_err)?;
|
||||
Ok(affected > 0)
|
||||
})
|
||||
.await
|
||||
.map_err(storage_err)?
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
||||
@@ -34,7 +34,7 @@ pub fn run(conn: &Connection) -> Result<()> {
|
||||
|
||||
// 迁移步骤链: 顺序执行,跳过已应用的版本(current_version < N 才跑)。
|
||||
// 新增版本时,在此数组追加一项 (N, migrate_vN) 即可,无需改逻辑。
|
||||
let steps: [(i32, fn(&Connection) -> Result<()>); 15] = [
|
||||
let steps: [(i32, fn(&Connection) -> Result<()>); 16] = [
|
||||
(1, migrate_v1),
|
||||
(2, migrate_v2),
|
||||
(3, migrate_v3),
|
||||
@@ -50,6 +50,7 @@ pub fn run(conn: &Connection) -> Result<()> {
|
||||
(13, migrate_v13),
|
||||
(14, migrate_v14),
|
||||
(15, migrate_v15),
|
||||
(16, migrate_v16),
|
||||
];
|
||||
|
||||
for (version, migrate_fn) in steps {
|
||||
@@ -276,6 +277,24 @@ fn migrate_v15(conn: &Connection) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// V16: 幂等补 ai_conversations.pinned 列(对话置顶,UX-17)
|
||||
///
|
||||
/// 侧栏置顶分组排序信号:前端按 pinned DESC, updated_at DESC 排,置顶在前。
|
||||
/// 纯元数据标记(同 archived),NOT NULL DEFAULT 0 保证老库行非 NULL,AiConversationRecord.pinned 为 bool。
|
||||
/// 用 PRAGMA 探测列存在性,缺失才 ALTER(同 v4/v5/v6/v8/v10/v11/v14/v15 模式),对新库/老库均安全。
|
||||
fn migrate_v16(conn: &Connection) -> Result<()> {
|
||||
if !column_exists(conn, "ai_conversations", "pinned") {
|
||||
conn.execute(
|
||||
"ALTER TABLE ai_conversations ADD COLUMN pinned INTEGER NOT NULL DEFAULT 0",
|
||||
[],
|
||||
)?;
|
||||
tracing::info!("v16: 补建 ai_conversations.pinned 列(对话置顶)");
|
||||
}
|
||||
conn.execute("INSERT INTO schema_version (version) VALUES (?)", [16])?;
|
||||
tracing::info!("迁移 v16 完成");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// V1 建表 SQL
|
||||
const V1_SQL: &str = "
|
||||
-- 想法表
|
||||
|
||||
@@ -160,6 +160,7 @@ pub struct AiConversationRecord {
|
||||
pub model: Option<String>,
|
||||
pub models: Option<String>, // 用过的所有 model(JSON 数组字符串,去重)
|
||||
pub archived: bool, // 是否归档(侧栏折叠展示)
|
||||
pub pinned: bool, // 是否置顶(排序置前;UX-17)
|
||||
pub prompt_tokens: Option<i64>, // 输入 token 累计(流式 usage 落库)
|
||||
pub completion_tokens: Option<i64>, // 输出 token 累计(流式 usage 落库)
|
||||
pub created_at: String,
|
||||
|
||||
Reference in New Issue
Block a user