新增: 媒体引用化存储与消息溯源

- media_store 模块: sha256 去重落盘 + 阿里云 OSS 双写(签名手写), OSS 失败降级本机引用
- 飞书图片入站: image 消息下载装配 vision 片, 3 次退避+120s 超时+残件清理防线
- 出站回传: 报告 [交付] 路径行自动上传飞书原生附件, 凭据/密钥名拦截兜底
- 溯源 V52: ai_messages.source 列, 飞书 msg_id/chat_id/发送者随消息落库
- spawn offload: 桌面粘贴图 base64 统一转落盘引用, SQLite 不进大字符串
- 引用统一 url 字段(https/file://)不自造 scheme, image_ref 发送前按需展开
This commit is contained in:
lxy
2026-10-11 07:33:18 +08:00
parent 8aefe3254c
commit 18e50c5af7
23 changed files with 1542 additions and 105 deletions
+17 -2
View File
@@ -64,7 +64,8 @@ pub fn run(conn: &Connection) -> Result<()> {
// conversation_checkpoints.token_total 语义回填。
// V51 = 待发箱 ai_outbox 表(定时消息与待发箱持久化 P0 地基):队列/定时统一实体,
// 调度器按 status+due_at 索引到点派发。纯新增表,可逆=DROP TABLE ai_outbox。
let steps: [(i32, fn(&Connection) -> Result<()>); 51] = [
// V52 = ai_messages.source 列(#958 消息溯源:外部渠道原始消息 ID JSON,NULL=桌面本端)。
let steps: [(i32, fn(&Connection) -> Result<()>); 52] = [
(1, migrate_v1),
(2, migrate_v2),
(3, migrate_v3),
@@ -122,6 +123,8 @@ pub fn run(conn: &Connection) -> Result<()> {
(50, migrate_v50),
// V51 = 待发箱 ai_outbox 表(定时消息与待发箱持久化 P0 地基)。
(51, migrate_v51),
// V52 = ai_messages 加 source 列(#958 消息溯源:飞书等外部渠道原始消息 ID)。
(52, migrate_v52),
];
for (version, migrate_fn) in steps {
@@ -1529,7 +1532,7 @@ fn migrate_v50(conn: &Connection) -> Result<()> {
/// 调度器启动时 reset_stale_sending 复位为 pending。
/// 迁移链顶点版本(单一真值来源):**新增迁移时必须同步 +1**,否则下方迁移测试会失败
/// (测试用它断言「全量迁移后 schema_version == 顶点」「重跑仍到顶点」——既防链中断,也免多处写死)。
pub const LATEST_SCHEMA_VERSION: i32 = 51;
pub const LATEST_SCHEMA_VERSION: i32 = 52;
fn migrate_v51(conn: &Connection) -> Result<()> {
if !table_exists(conn, "ai_outbox") {
@@ -1569,6 +1572,18 @@ fn migrate_v51(conn: &Connection) -> Result<()> {
Ok(())
}
/// V52: ai_messages 加 source 列(#958 消息溯源)。TEXT JSON(MessageSource 序列化),
/// NULL=桌面本端。纯加列零回退风险(老数据恒 NULL=既有语义)。
fn migrate_v52(conn: &Connection) -> Result<()> {
if !column_exists(conn, "ai_messages", "source") {
conn.execute("ALTER TABLE ai_messages ADD COLUMN source TEXT", [])?;
tracing::info!("v52: ai_messages 加 source 列(消息溯源)");
}
conn.execute("INSERT OR IGNORE INTO schema_version (version) VALUES (?)", [52])?;
tracing::info!("迁移 v52 完成");
Ok(())
}
/// 回填 conversation_checkpoints.token_total:按 snapshot JSON 内逐条消息的
/// prompt_tokens+completion_tokens 求真实总量。
///