编辑 | blame | 历史 | 原始文档

报告模板版本管理 - 前端联调方案(保存校验 + 停用约束)

涉及页面

  • 报告模板设计器(保存 / 另存新版本,走 createupdate
  • 报告模板详情 → 版本列表(发布 / 回滚 / 停用)

本次变更概述

qc_report_template_version 的写入口新增两条**拒绝规则**,各对应一个新错误码。
两条都不是新增字段、也不是新增接口,而是把「以前会静默存下 / 存下后才炸」的情况改成**当场报错**。
前端需要做的是:把后端返回的 msg 原样展示(文案已可直接给用户看),并保证这两种失败**不弹通用「系统异常」**。

接口 方法 新增拒绝规则 错误码
/qc-report/template-version/create POST 画布含不允许渲染的内容 1_070_101_009
/qc-report/template-version/update PUT 同上 1_070_101_009
/qc-report/template-version/disable POST 目标是模板的**当前版本** 1_070_101_010

createupdate 共用同一段 Schema 校验,两条入口行为一致:**只要提交的 Schema 不合格,一律不落库**。

规则一:画布内容安全校验(1_070_101_009

挡的是什么

报告产物是拼字符串拼出来的,模板画布里有三处内容会**不经转义**直接进入 HTML:

  1. 节点的标签名(如 tagName: "h2");
  2. 属性名(属性**值**会转义,属性**名**不会);
  3. 整段 CSSstyles 写成字符串时会被原样塞进 <style>)。

模板画布又是用户可编辑数据,所以这三处都是可被写入的注入面。

校验规则

维度 规则
标签名 必须落在白名单内:常规排版与文本标签(div/p/span/h1~h6/img/hr、表格一族、列表一族、常见行内语义标签)。脚本、样式、内嵌页面、表单类标签一律拒绝
标签名 需**整体**匹配白名单,"img src=x onerror=alert(1)" 这种「标签名里塞属性」的写法会被拒
属性名 只允许字母 / 数字 / - _ : .,且不能以数字开头;含引号或空格的属性名会被拒(它能顶出一个新属性)
属性名 on 开头的一律视为事件属性,拒绝
styles 必须是**样式规则数组**;写成一段 CSS 文本会被拒(改样式走设计器的样式面板,保存后自然是规则数组)
selectors / mediaText / 样式属性名与属性值 出现 < 即拒绝(</style> 能提前闭合样式块,把后面的内容变成真 HTML)

不会误杀的三种情况

设计器正常保存的画布**不会**被拦,以下三类在实现里被明确排除,前端无需特殊处理:

  • textnode 节点上的 tagName:渲染器根本不读它;
  • GrapesJS 挂在节点上的 docEl / head 元数据:渲染器不读,校验也只沿 components 走;
  • 存量画布:已对运行库中全部真实版本实测通过。

报错文案

一次最多返回 8 条问题,每条都带**位置**与**违规内容**,形如:

模板画布里有不允许出现的内容,已拒绝保存:画布「根 > 第 1 个组件」的标签是「script」,
报告不支持该标签。可用的只有常规排版标签(div/p/span/h1~h6/img/hr 与表格、列表、行内文本标签),
脚本、样式、内嵌页面、表单类标签一律不允许。请在设计器中删掉这些内容后重新保存

多条问题以 连接。改完再保存一次即可看到下一批。

规则二:不能停用「当前版本」(1_070_101_010

挡的是什么

模板表上的 current_version 指向「出件时默认用哪个版本」。
停用某个版本**不会**清空 current_version,于是停用当前版本会造出一份**自相矛盾**的模板:
current_version 还指着它,它却已经不可用。这种模板自此出不了报告,
而且失败发生在**出件那一刻**(报 1_070_101_003),用户不会想到起因是之前那次停用。

因此 disable 直接拒绝,**不会**顺手替用户改掉 current_version——
「改哪个版本」是用户的决定,不该由「停用」这个动作悄悄代劳。

报错文案

版本「v1.1」是模板「来料检验报告模板」当前正在使用的版本,不能停用。
请先到该模板的版本列表里对另一个已发布版本执行「回滚」,把当前版本切走,再停用本版本;
若要让整个模板停止使用,请改为停用模板本身

文案里给出了**两条可执行出路**,前端不需要自己组织引导话术。

被拒的判定口径

  • 比较对象是**模板表 current_version 的字符串**与**该版本记录的 version**;
  • 只要相等就拒绝,**不看版本是否已发布**(草稿版本同样可能被 current_version 指到);
  • 停用**非当前版本**的已发布版本依然允许,行为不变。

字段展示规则

本次无新增字段,无需调整列表或表单列。

场景 前端表现
保存模板报 1_070_101_009 msg 全文提示(可能含多条,用 分隔);**不要**截断成「保存失败」
停用版本报 1_070_101_010 msg 全文提示;提示文案已含后续操作步骤,无需另加指引
其它错误码 行为不变

业务规则说明

场景 规则
新建版本 / 更新版本时 Schema 为空 允许(先建版本后设计),不触发画布校验
新建版本 / 更新版本时 Schema 非空但缺 schemaVersion 拒绝,1_070_101_004
画布有问题 拒绝保存,1_070_101_009,**不落库**
更新**已发布**版本 仍然拒绝,1_070_101_002(改动只能另存新草稿),与本次变更无关
停用当前版本 拒绝,1_070_101_010
停用非当前版本 正常停用
停用整个模板 走「停用模板」入口,不受本次变更影响

注意事项

  • 错误提示不要吞掉:这两条都是**用户在编辑器里的操作反馈**,必须把后端 msg 原样展示。
    画布问题一次最多 8 条,多条时建议按 拆行展示,便于逐条处理。
  • 对校验上线前就已入库的脏数据没有兜底:校验只在保存时执行,历史版本不会被回溯检查。
    若怀疑存量版本有问题,需要另行核查(出件链路不受影响,仍按原样渲染)。
  • 1_070_101_009 的定位信息是「第 N 个组件」:指该组件在其父节点 components 数组中的序号(从 1 开始),
    路径形如 根 > 第 2 个组件 > 第 1 个组件;层级过深时中间部分会折叠为
  • 画布校验**不改变渲染结果**:渲染引擎与前端 engine/render.ts 的逐字一致性和存量报告的
    regenerate 复现性都不受影响,本次只在**写入侧**加闸门。