Files
ele-HeiXiu/src/infrastructure/storage/param-store.ts
YoungestSongMo e4c9ae8fc6 feat: 三栏比例缩放 + SeedanceContent视频上传 + 本地存储基建 (0.0.22)
三栏布局
- ResizeObserver 等比例分配替代固定默认值恢复
- 修复拖拽+窗口调整竞态卡死(needsRecalcRef 延迟补算)
- containerRef.current 动态读取 DOM 替代闭包 el

Seedance2Content
- 从占位文本重构为内容编排控件(文本+图片/视频/音频上传)
- 根据 spec.image_files/video_files/audio_files 动态渲染上传区域
- 值 JSON 序列化 → 提交时转 ContentItem[](text/image_url/video_url/audio_url)

图片上传兼容视频文件
- buildAccept 根据 file_filter 区分 image/video 前缀
- createMediaBeforeUpload 动态放行 video/* MIME
- 公共模块 media-upload-utils.ts

本地存储基建
- sql.js (WASM SQLite) + safeStorage 加密持久化
- 8个IPC文件操作通道 + resolveSafe 路径穿越防护
- <userData>/heixiu-data/ 沙箱 + JWT用户隔离
- 7表DDL + 游标分页 + LRU媒体淘汰

其他
- 修复 errorMessageRef 渲染期写入警告
- 媒体预览回退直接URL(<img>无CORS限制)+ output_type类型补充
- CSP wasm-unsafe-eval + https: CDN媒体源
2026-06-18 19:22:28 +08:00

81 lines
2.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// infrastructure/storage/param-store.ts — 参数历史本地存储
//
// 职责:
// - 任务提交后保存输入参数快照task_id 主键)
// - 任务完成后回填输出数据
// - 按模型查询历史参数列表(回填菜单数据源)
// ============================================================
import { execute, queryAll, queryOne } from './db-core';
import { getCurrentUserId } from './user-scope';
import type { ParamHistoryEntry } from './types';
const MAX_PER_MODEL = 20;
// ---------- API ----------
export function saveParamSnapshot(
taskId: string,
modelId: string,
modelName: string,
inputParams: Record<string, unknown>,
tag?: string,
): void {
const uid = getCurrentUserId();
execute(
`INSERT OR REPLACE INTO param_history (task_id, user_id, model_id, model_name, input_params_json, tag, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
[taskId, uid, modelId, modelName, JSON.stringify(inputParams), tag || null, Date.now()],
);
// 裁剪旧记录
prune(modelId, uid);
}
export function fillOutputData(taskId: string, outputData: Record<string, unknown>): void {
execute(
`UPDATE param_history SET output_data_json = ? WHERE task_id = ?`,
[JSON.stringify(outputData), taskId],
);
}
export function getParamByTaskId(taskId: string): ParamHistoryEntry | null {
return queryOne<ParamHistoryEntry>(`SELECT * FROM param_history WHERE task_id = ?`, [taskId]);
}
export function getParamHistory(modelId: string, limit = 10): ParamHistoryEntry[] {
const uid = getCurrentUserId();
return queryAll<ParamHistoryEntry>(
`SELECT * FROM param_history WHERE user_id = ? AND model_id = ? ORDER BY created_at DESC LIMIT ?`,
[uid, modelId, limit],
);
}
export function parseParams(entry: ParamHistoryEntry): Record<string, unknown> {
try { return JSON.parse(entry.input_params_json) as Record<string, unknown>; } catch { return {}; }
}
export function deleteParamHistory(id: number): void {
execute(`DELETE FROM param_history WHERE id = ?`, [id]);
}
export function clearUserParams(): void {
execute(`DELETE FROM param_history WHERE user_id = ?`, [getCurrentUserId()]);
}
// ---------- 内部 ----------
function prune(modelId: string, userId: string): void {
try {
const rows = queryAll<ParamHistoryEntry>(
`SELECT id FROM param_history WHERE user_id = ? AND model_id = ? ORDER BY created_at DESC`,
[userId, modelId],
);
if (rows.length <= MAX_PER_MODEL) return;
const toDelete = rows.slice(MAX_PER_MODEL);
for (const r of toDelete) {
if (r.id) execute(`DELETE FROM param_history WHERE id = ?`, [r.id]);
}
} catch { /* ignore */ }
}