package cn.iocoder.yudao.module.qcreport.dal.dataobject.version; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import java.util.List; import java.util.Map; /** * 报告模板 Schema —— 设计器产物,也是报告出件的唯一输入。 *

* 模板不保存裸 HTML。保存的是「画布数据 + 业务语义」,由渲染引擎据此生成 HTML, * 这样同一份模板在不同渲染环境下产物一致,也才有可能做服务端 Sanitization。 * *

字段分工

* * * * * *
{@link #grapes}GrapesJS 原始项目数据。设计器靠它无损还原画布,渲染引擎也读它(正文结构在这里)
{@link #components}质量组件业务语义清单。渲染不读,供设计器定位组件、供后续 AI 生成模板时理解结构
{@link #rules}判定规则。渲染前由判定器求值,决定每项 PASS/FAIL 与报告结论
{@link #page}纸张与页边距,渲染时换算成 CSS {@code @page}
* *

语义层是有意的子集(重要)

* {@link #components} 只表达「用已注册的质量组件搭出来的扁平结构」, * 不表达任意 HTML——用户手工拖进画布的裸表格、自定义 div 不在其内。 * 这是有意收窄的边界,不是实现疏漏:有这条边界,AI 生成模板时才能被约束成 * 「只能用已注册组件当积木」,而不是吐一段谁也不敢渲染的 HTML。 * 任何「给语义层加个万能 customHtml 字段」的改动都在拆这条边界,需要先讨论。 * *

兼容性

*

* {@code schema_json} 列里已经存着含 1.0 字段(dataSources / bindings / styles)的历史 JSON, * 所以这里显式标注 {@link JsonIgnoreProperties} 容错。 *

* 说明一句,免得后人以为它必不可少:去掉这个注解,兼容性测试也是绿的—— * 读库那条链路用的 {@code Jackson3TypeHandler} 自己 new 的 ObjectMapper, * 而 Jackson 3 的 {@code FAIL_ON_UNKNOWN_PROPERTIES} 默认就是关的。 * 真正需要它防的是另外两条:Spring 反序列化请求体时用的 ObjectMapper(配置不受本模块控制), * 以及将来有人给类型处理器换一个更严格的 Mapper。留着是把意图写明,不是摆设。 * *

版本历史

* */ @Data @JsonIgnoreProperties(ignoreUnknown = true) public class ReportTemplateSchema { /** * 当前 Schema 版本号。 *

* 与前端 {@code designer/constants.ts} 的 {@code SCHEMA_VERSION} 必须一致: * 两端各写一份是历史原因(后端不参与设计器构建),改这里就要同步改那边。 */ public static final String SCHEMA_VERSION = "1.1"; /** * Schema 版本,用于后续兼容升级 */ private String schemaVersion; /** * 纸张与页边距配置 */ private Page page; /** * GrapesJS 原始画布数据(保留以便设计器无损还原) */ private Map grapes; /** * 质量组件业务语义清单,按文档顺序排列 */ private List components; /** * 判定规则定义。 *

* 元素形状为 {@code {id, name, scope, expression, enabled}},由 * {@code QualityRuleDefinition.from(Map)} 宽松读取——历史数据里可能有缺字段的规则, * 读不动的跳过而不是让整份模板打不开。 */ private List> rules; /** * 纸张与页边距配置 */ @Data public static class Page { /** * 纸张尺寸:A3 / A4 / A5 / Letter */ private String size; /** * 纸张方向:portrait / landscape */ private String orientation; /** * 页边距,单位 mm */ private Margin margin; } /** * 页边距,单位 mm */ @Data public static class Margin { private Double top; private Double right; private Double bottom; private Double left; } /** * 画布上的一个质量组件节点。 *

* 只记「是谁、在哪、什么属性」,不记子节点——画布的嵌套结构以 {@link #grapes} 为准, * 这里再存一份树就出现两个真相来源,迟早对不上。 */ @Data public static class QualityComponentNode { /** 画布内组件 id */ private String id; /** 组件在文档中的顺序,供绑定与定位使用 */ private Integer index; /** 组件类型,即画布节点上的 data-quality-type */ private String qualityType; /** * 组件的业务属性。 *

* 取值统一是 {@code data-qc-*} / {@code data-quality-type} 这类字符串属性, * 用 Object 是因为 GrapesJS 的 getAttributes() 原样带出,可能有非字符串值。 */ private Map attributes; } }