几何画板工程持久化与版本管理方案:变更监听、自动保存与版本恢复

老师在题目编辑页补了一条辅助线,保存后关闭页面。第二天重新打开,辅助线还在,标签位置也没有变化。已经发给学生的试卷则继续显示审核过的原图。

要同时满足这两个要求,业务系统需要保存完整工程,并让草稿、发布版本和导出图片各自指向明确的记录。画板负责编辑与读写,后端负责持久化、冲突检测和版本保留。

本文以大角几何内嵌编辑器为例,给出一套可以接入题库或教研产品的保存方案。

保存对象应是完整工程

几何工程包含构造定义,也包含变量当前值、样式、文字和相机。只保存对象定义,可能丢失拖动后的位置或当前观察角度;只保存 PNG,则无法恢复可编辑状态。

从编辑器读取完整内容:

javascript 复制代码
const content = await editor.document.getContent();

加载已保存的工程:

javascript 复制代码
await editor.document.loadContent(content);

工程格式与保留字段见工程文件数据协议。保存时保留完整内容,不要在业务后端凭经验筛掉"暂时没用"的字段。

PNG 和缩略图可以作为工程版本的衍生产物保存,方便列表预览。用户进入编辑页时读取源工程,进入已发布题目时读取发布记录指定的版本。

数据模型区分当前草稿与历史快照

可以从三类记录开始设计。下表是业务数据模型示例,不是大角几何接口字段:

记录 关键字段 负责什么
工程主记录 documentId、headVersion、publishedVersion 关联当前草稿与已发布版本
版本快照 documentId、version、content、author、createdAt、requestId 保存不可变工程及变更来源
导出记录 documentId、version、renderProfile、imageKey 把图片绑定到确切工程版本

首个实现可以直接把完整工程作为 JSON 存入数据库;内容较大时,也可以将快照保存到对象存储,数据库记录存储位置与校验值。两种方式都要确保只有完成存储的版本才能成为当前版本。

自动保存频繁的产品,可以将短期草稿检查点与正式版本分层管理。保留周期按产品需求制定;用户明确保存、审核和发布的版本应有清晰的追踪记录。

当前草稿、已发布版本与各自图片分别关联(AI 生成业务系统设计示意)。

用变更事件标记未保存状态

编辑器提供 contentChange 事件。它适合触发防抖保存,并让宿主显示"有未保存修改"。正式保存前再读取一次 getContent(),可以取得当前完整内容。

防抖只减少请求数量,还需要串行化写入。否则,前一次网络请求较慢,可能在新请求之后完成,造成保存顺序与用户操作顺序不一致。

下面给出保存控制器的核心示例。假设 editor 已初始化,record.version 来自业务后端;宿主页面提供 save-state 状态元素。后端按下一节实现版本检查和请求去重。

javascript 复制代码
const state = document.getElementById('save-state');
let baseVersion = record.version;
let changeSeq = 0;
let savedSeq = 0;
let timer;
let queue = Promise.resolve();
let blocked = false;
let uncertain = false;

function show(text) { state.textContent = text; }

async function writeSnapshot(operation) {
  for (let attempt = 0; attempt < 2; attempt++) {
    try {
      const response = await fetch(`/api/geometry-docs/${record.id}/versions`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(operation),
        signal: AbortSignal.timeout(15_000),
      });
      if (response.status === 409) {
        blocked = true;
        throw new Error('版本冲突,请先比较本地内容与服务器版本');
      }
      if (!response.ok) throw new Error('保存请求未完成');
      const result = await response.json();
      if (!Number.isInteger(result.version)) throw new Error('缺少确认版本');
      return result;
    } catch (error) {
      if (blocked) throw error;
      if (attempt === 1) {
        uncertain = true;
        throw new Error(`保存结果待确认,请查询请求 ${operation.requestId}`);
      }
      // 复用同一 requestId 和快照,避免响应丢失造成重复版本。
    }
  }
}

function saveLatest() {
  const task = queue.then(async () => {
    if (blocked || uncertain) throw new Error('请先处理冲突或确认上次保存结果');
    const seq = changeSeq;
    const content = await editor.document.getContent();
    const result = await writeSnapshot({
      requestId: crypto.randomUUID(), baseVersion, content,
    });
    baseVersion = result.version;
    savedSeq = Math.max(savedSeq, seq);
    show(savedSeq === changeSeq ? '已保存' : '仍有修改待保存');
    return result;
  });
  queue = task.catch(() => {});
  return task;
}

const offChange = editor.on('contentChange', () => {
  changeSeq += 1;
  show('有未保存修改');
  clearTimeout(timer);
  timer = setTimeout(() => {
    saveLatest().catch(error => show(error.message));
  }, 1000);
});

一秒防抖、十五秒超时和两次尝试都是示例参数。uncertain 表示网络中断后无法确定保存是否已提交,后端需要提供按 requestId 查询结果的能力。确认成功后更新版本并解除状态;确认未提交后,才允许继续保存。不要在结果不明时直接创建另一份相同请求。

同样,版本冲突需要先展示差异或保留本地副本,不能在自动保存循环里反复覆盖。这个控制器处理正常编辑的写入顺序,冲突解决界面由宿主实现。

宿主界面区分未保存、已保存、结果待确认和版本冲突(AI 生成交互设计示意)。

手动保存也走同一个写入队列

大角几何编辑器的 save 事件分为请求与成功两个阶段。监听器应在请求阶段等待后端完成,成功后返回确认:

javascript 复制代码
const offSave = editor.on('save', async event => {
  if (event.stage !== 'request') return;
  clearTimeout(timer);
  try {
    await saveLatest();
    return { status: 'success' };
  } catch (error) {
    show(error.message);
    return { status: 'error', message: error.message };
  }
});

自动保存与手动保存复用同一个队列和版本状态,可避免两条保存入口并行写入。保存期间出现新编辑时,宿主应继续显示存在未保存修改;如果需要按钮的完成状态严格对应点击时的内容,可短暂阻止编辑并等待保存完成。

监听和返回格式见编辑模式文档。页面退出时取消订阅并清理定时器;有未保存内容时提示用户处理,不能依赖页面关闭请求一定送达。由代码发起的工程替换或 AI 结果加载,也应在宿主流程中显式安排保存。

后端用事务阻止旧版本覆盖

收到 { requestId, baseVersion, content } 后,后端先验证用户对该工程的写入权限,再在事务中处理以下步骤:

  1. 按工程和 requestId 查找已完成请求;相同请求返回原结果,并核对内容摘要一致。
  2. 锁定或以条件更新方式检查当前版本是否等于 baseVersion。
  3. 不相等则返回 409;相等则插入新快照,并把主记录推进到新版本。
  4. 事务提交后返回已确认的版本号。

请求去重必须先于旧版本判定。否则第一次写入成功但响应丢失后,重试会被误判为版本冲突。数据库还应对工程与请求 ID 设置唯一约束,覆盖重复请求并发到达的情况。

如果快照存放在对象存储,可以先写入不可变对象,再提交数据库记录。数据库提交失败留下的未引用对象由后台清理;不能在快照尚未可读时提前更新当前版本。

两个标签页同时编辑时,后写入的一方应看到冲突提示,并可以保存为副本或人工比较。几何对象有依赖关系,第一版产品可以先提供安全的人工处理流程,再考虑自动合并。

发布版本与版本恢复分开处理

审核通过后,将 publishedVersion 指向确认版本。教师之后继续编辑,只推进草稿,已经发布的试卷继续引用原有快照。

恢复历史版本也可以采用"创建新版本"的方式。例如当前为第 8 版,用户希望恢复第 5 版:读取第 5 版内容,以当前第 8 版为基线提交,创建第 9 版,并记录恢复来源。这样既恢复了内容,也保留第 6 至第 8 版的历史。

恢复历史内容时创建新版本,并保留中间版本记录(AI 生成方案示意)。

编辑器的撤销、重做用于当前编辑过程;服务端版本用于跨会话追踪。两者应在界面和存储设计上分别处理。图片同样应关联版本,恢复工程后按需要重新渲染,避免继续展示旧缩略图。

验证重新打开与故障恢复

验收先检查"修改---保存---关闭---重开",再补三类异常:保存后响应丢失、两个页面同时修改、保存过程中继续编辑。每类异常都要检查服务器最终版本和页面状态是否一致。

最后从一个历史版本恢复内容,确认它产生新的记录,并检查已发布题目是否仍读取原版本。至此,几何工程就能作为业务资产持续修订,而每张正式出图也有可追踪的来源。

相关推荐
试剂界的爱马仕7 小时前
α-Cyano-4-hydroxycinnamic acid(CHCA)用作 MALDI-TOF 基质的配制与使用
大数据·人工智能·深度学习·学习
强哥聊算力7 小时前
《算力江湖·Intel帝国往事》第三集:安迪·格鲁夫的铁血时代:Intel为什么能把对手逼到墙角?
运维·人工智能·科技·算法·ai·gpu算力
Raymond16887 小时前
零样本TTS长视频配音的工程取舍:自回归、非自回归与韵律边界
人工智能·数据挖掘·回归·音视频·音频
大熊背7 小时前
人工智能中的数学之美---从向量角度理解深度学习中的感知机
人工智能·深度学习
杭州华望MBSE7 小时前
AI将载着MBSE驶向哪里?从前台工具转向工程语义与验证层
人工智能·大语言模型·mbse·sysml·国产工业软件
我喜欢就喜欢7 小时前
AI与教育:一场双向塑造的相互性变革
人工智能
指针向南7 小时前
PDF 转 HTML 免费5种方法,体积0.5%到368%
图像处理·人工智能·计算机视觉·pdf
星核0penstarry7 小时前
企业 API 向后兼容实战:版本控制、废弃字段通知与灰度发布策略
人工智能·后端·api·接口隔离原则·版本控制·架构设计·后端架构
jerrywus7 小时前
AI 写的代码能跑,为什么接手的人想跑?
前端·agent·claude