package cn.iocoder.yudao.module.qcreport.engine; import cn.iocoder.yudao.module.qcreport.dal.dataobject.version.ReportTemplateSchema; import cn.iocoder.yudao.module.qcreport.engine.context.ReportContext; import cn.iocoder.yudao.module.qcreport.engine.render.HtmlRenderer; import cn.iocoder.yudao.module.qcreport.engine.render.RenderOutcome; import cn.iocoder.yudao.module.qcreport.engine.rule.RuleEngine; import cn.iocoder.yudao.module.qcreport.engine.rule.RuleEvaluator; import cn.iocoder.yudao.module.qcreport.engine.rule.QualityRuleDefinition; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * 渲染引擎入口。 *
* 只做一件事:把落库的模板 Schema 翻译成引擎的内部结构,然后渲染。 * 引擎本身不依赖 Spring、不落库、不做权限——它只吃「Schema + 上下文」吐 HTML, * 这样批量生成、定时任务、其它模块调用都能走同一个入口。 */ public final class QualityReportEngine { private QualityReportEngine() { } /** 渲染报告:判定 → 生成正文 → 组装文档 */ public static RenderOutcome render(ReportTemplateSchema schema, ReportContext context) { return HtmlRenderer.render(grapesOf(schema), pageOf(schema), context, rulesOf(schema)); } /** * 模板是否已经有可渲染的画布内容。 *
* 判据与渲染器读画布的那一行严格一致——{@link HtmlRenderer#render} 取的是 * {@code pages[0].frames[0].component},这里就用 {@link Paths#readPath} 取同一个位置, * 而不是退化成「grapes 这个 Map 非空」。 *
* 为什么不能只看非空:GrapesJS 把「打开过但一个组件都没放」的项目存成
* {@code {"assets":[],"styles":[]}},它非空、却没有 frames;渲染器取不到根组件时
* 直接把 body 渲染成空串——不报错,用户拿到一张白纸 PDF。出件前的这个检查就是为了
* 把这种版本挡在渲染之前,判据必须和渲染器对齐,否则挡不住。
*/
public static boolean hasCanvas(ReportTemplateSchema schema) {
return Paths.readPath(grapesOf(schema), "pages[0].frames[0].component") instanceof Map, ?>;
}
/** GrapesJS 项目数据 */
public static Map
* 除了语法,还拦白名单外的函数调用:这类规则一旦存下去,
* 每张报告都会带上一条「无法执行」,不如在设计阶段就退回去。
*
* 空表达式的规则也退回去。这条曾经是放行的——当时设计器还没有规则编辑界面,
* 报了错用户无处可改,只会卡在保存不了,等于把「一条坏数据」升级成「整份模板不能保存」。
* 规则编辑界面做出来之后这个理由不成立了:用户在设计器里删掉这条规则就能继续保存,
* 所以改成明确报错。读取仍然宽容({@link #rulesOf} 不会因为一条坏规则让整份模板打不开),
* 执行期也照常把它报出来(「第 N 项「X」的规则「Y」无法执行:规则表达式为空」)。
*/
public static List