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 —— 设计器产物,也是报告出件的唯一输入。
|
* <p>
|
* 模板不保存裸 HTML。保存的是「画布数据 + 业务语义」,由渲染引擎据此生成 HTML,
|
* 这样同一份模板在不同渲染环境下产物一致,也才有可能做服务端 Sanitization。
|
*
|
* <h3>字段分工</h3>
|
* <table>
|
* <tr><td>{@link #grapes}</td><td>GrapesJS 原始项目数据。设计器靠它无损还原画布,<b>渲染引擎也读它</b>(正文结构在这里)</td></tr>
|
* <tr><td>{@link #components}</td><td>质量组件业务语义清单。渲染不读,供设计器定位组件、供后续 AI 生成模板时理解结构</td></tr>
|
* <tr><td>{@link #rules}</td><td>判定规则。渲染前由判定器求值,决定每项 PASS/FAIL 与报告结论</td></tr>
|
* <tr><td>{@link #page}</td><td>纸张与页边距,渲染时换算成 CSS {@code @page}</td></tr>
|
* </table>
|
*
|
* <h3>语义层是<strong>有意的子集</strong>(重要)</h3>
|
* {@link #components} 只表达「用已注册的质量组件搭出来的扁平结构」,
|
* <b>不表达任意 HTML</b>——用户手工拖进画布的裸表格、自定义 div 不在其内。
|
* 这是有意收窄的边界,不是实现疏漏:有这条边界,AI 生成模板时才能被约束成
|
* 「只能用已注册组件当积木」,而不是吐一段谁也不敢渲染的 HTML。
|
* 任何「给语义层加个万能 customHtml 字段」的改动都在拆这条边界,需要先讨论。
|
*
|
* <h3>兼容性</h3>
|
* <p>
|
* {@code schema_json} 列里已经存着含 1.0 字段(dataSources / bindings / styles)的历史 JSON,
|
* 所以这里显式标注 {@link JsonIgnoreProperties} 容错。
|
* <p>
|
* 说明一句,免得后人以为它必不可少:<b>去掉这个注解,兼容性测试也是绿的</b>——
|
* 读库那条链路用的 {@code Jackson3TypeHandler} 自己 new 的 ObjectMapper,
|
* 而 Jackson 3 的 {@code FAIL_ON_UNKNOWN_PROPERTIES} 默认就是关的。
|
* 真正需要它防的是另外两条:Spring 反序列化请求体时用的 ObjectMapper(配置不受本模块控制),
|
* 以及将来有人给类型处理器换一个更严格的 Mapper。留着是把意图写明,不是摆设。
|
*
|
* <h3>版本历史</h3>
|
* <ul>
|
* <li><b>1.0</b>:初版。含 dataSources / bindings / styles 三个字段,但它们始终是空数组,
|
* 既无人写入也无人读取,属死数据。</li>
|
* <li><b>1.1</b>:删除上述三个死字段,{@code components} 由 {@code Map} 改为强类型
|
* {@link QualityComponentNode}。因死字段原本就恒为空数组,1.0 → 1.1 无需数据迁移。</li>
|
* </ul>
|
*/
|
@Data
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
public class ReportTemplateSchema {
|
|
/**
|
* 当前 Schema 版本号。
|
* <p>
|
* 与前端 {@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<String, Object> grapes;
|
/**
|
* 质量组件业务语义清单,按文档顺序排列
|
*/
|
private List<QualityComponentNode> components;
|
/**
|
* 判定规则定义。
|
* <p>
|
* 元素形状为 {@code {id, name, scope, expression, enabled}},由
|
* {@code QualityRuleDefinition.from(Map)} 宽松读取——历史数据里可能有缺字段的规则,
|
* 读不动的跳过而不是让整份模板打不开。
|
*/
|
private List<Map<String, Object>> 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;
|
|
}
|
|
/**
|
* 画布上的一个质量组件节点。
|
* <p>
|
* 只记「是谁、在哪、什么属性」,不记子节点——画布的嵌套结构以 {@link #grapes} 为准,
|
* 这里再存一份树就出现两个真相来源,迟早对不上。
|
*/
|
@Data
|
public static class QualityComponentNode {
|
|
/** 画布内组件 id */
|
private String id;
|
/** 组件在文档中的顺序,供绑定与定位使用 */
|
private Integer index;
|
/** 组件类型,即画布节点上的 data-quality-type */
|
private String qualityType;
|
/**
|
* 组件的业务属性。
|
* <p>
|
* 取值统一是 {@code data-qc-*} / {@code data-quality-type} 这类字符串属性,
|
* 用 Object 是因为 GrapesJS 的 getAttributes() 原样带出,可能有非字符串值。
|
*/
|
private Map<String, Object> attributes;
|
|
}
|
|
}
|