低代码平台的智能校验引擎:从类型推导到业务规则
一、低代码校验的独特挑战
传统表单校验解决的是"用户输入是否合法"的问题,范围局限在单个字段的格式校验上。低代码平台的校验场景要复杂得多:校验对象不是单个输入框,而是一张画布上由用户以拖拽方式搭建出来的整个页面配置。配置本身的合法性、组件之间的约束关系、数据流的完整性——这些都需要在保存或发布配置时做整体校验。
flowchart TD A[画布配置 JSON] --> B{校验引擎入口} B --> C[层级一: 类型推导校验] B --> D[层级二: 组件约束校验] B --> E[层级三: 业务规则校验] C --> F{通过?} F -->|是| D F -->|否| G[编译时错误: 阻塞保存] D --> H{通过?} H -->|是| E H -->|否| I[警告级别: 允许保存但标注风险] E --> J{通过?} J -->|是| K[校验通过] J -->|否| L[阻断发布: 必须修正]三层层级从"类型正确"到"逻辑合理"依序展开。低层级错误直接阻断(类型不对意味着运行时报错),高层级问题标记为警告或阻断发布(取决于严重程度)。
二、类型推导:配置结构的静态校验
低代码画布的输出通常是一份 JSON 配置,描述组件树的结构。类型推导校验的目标是:在保存之前,用 TypeScript 类型系统验证这份 JSON 是否符合预期的 Schema 约束。
/** * 低代码画布输出配置的类型定义 * 校验引擎据此验证配置的合法性 */ interface PageConfig { version: string; components: ComponentConfig[]; dataSources: DataSourceConfig[]; interactions: InteractionConfig[]; } interface ComponentConfig { id: string; type: 'Button' | 'Input' | 'Select' | 'Table' | 'Form'; props: Record<string, unknown>; /** 子组件:支持嵌套布局 */ children?: ComponentConfig[]; /** 绑定的数据源 id */ dataBindings?: DataBinding[]; } interface DataBinding { /** 组件属性名 */ prop: string; /** 绑定的数据源 ID */ sourceId: string; /** 数据路径:如 'data.list[0].name' */ path: string; } interface DataSourceConfig { id: string; type: 'api' | 'state' | 'computed'; /** API 配置 */ config?: { url: string; method: 'GET' | 'POST'; params?: Record<string, unknown>; }; } interface InteractionConfig { event: string; // 触发事件名 sourceId: string; // 触发组件 ID actions: ActionConfig[]; // 事件触发的动作列表 } interface ActionConfig { type: 'navigate' | 'openModal' | 'updateState' | 'apiCall'; payload: Record<string, unknown>; }校验引擎的第一步是将配置 JSON 反序列化后跑通 TypeScript 的satisfies检查:
/** * 类型推导校验:验证 JSON 是否符合 Schema 约束 */ import Ajv, { ValidateFunction, ErrorObject } from 'ajv'; class ConfigValidator { private ajv: Ajv; constructor(schemas: Record<string, object>) { this.ajv = new Ajv({ allErrors: true, strict: false }); // 注册各组件的 Props Schema for (const [type, schema] of Object.entries(schemas)) { this.ajv.addSchema(schema, `component/${type}`); } } validate(config: PageConfig): ConfigValidationReport { const errors: ValidationError[] = []; for (const component of config.components) { const componentErrors = this.validateComponent(component); errors.push(...componentErrors); } // 校验数据绑定引用的完整性 const dataSourceIds = new Set(config.dataSources.map(ds => ds.id)); const bindingErrors = this.validateBindings(config.components, dataSourceIds); errors.push(...bindingErrors); return { valid: errors.length === 0, errors, // 按严重级别分类 blocking: errors.filter(e => e.severity === 'error'), warnings: errors.filter(e => e.severity === 'warning'), }; } private validateComponent( component: ComponentConfig, path: string = 'root' ): ValidationError[] { const errors: ValidationError[] = []; const schemaKey = `component/${component.type}`; try { this.ajv.validate(schemaKey, component.props); if (this.ajv.errors) { for (const err of this.ajv.errors) { errors.push(this.formatAjvError(err, `${path}/${component.id}`)); } } } catch (e) { errors.push({ path: `${path}/${component.id}`, message: `类型校验异常: ${(e as Error).message}`, severity: 'error', }); } // 递归校验子组件 if (component.children) { for (const child of component.children) { errors.push( ...this.validateComponent(child, `${path}/${component.id}`) ); } } return errors; } }三、组件约束:跨层级关系校验
类型正确只是最低门槛。组件之间的约束关系才是工程上的重点:Table 组件必须绑定到一个 API 数据源;Button 组件必须有至少一个 action 在 Interaction 中被注册;Form 组件内 Input 必须有对应的字段名。
flowchart LR A[Table 组件] -->|约束: 必须绑定 API 数据源| B{检查 dataBindings} A -->|约束: 必须声明 columns| C{检查 props.columns} D[Form 组件] -->|约束: 子组件必须含 name| E{检查 children[name]} F[Button 组件] -->|约束: 必须注册 Interaction| G{检查 interactions 引用} B --> H[通过 / 阻断] C --> H E --> H G --> H约束校验引擎的实现方式有两种:声明式规则引擎和函数式断言。对于规则数量在 50 条以内的场景,函数式断言更直接可控:
/** * 组件约束校验:基于断言函数检查跨层级关系 */ type ConstraintRule = ( component: ComponentConfig, ctx: ValidationContext ) => ValidationError[]; interface ValidationContext { config: PageConfig; dataSourceMap: Map<string, DataSourceConfig>; interactionMap: Map<string, InteractionConfig>; } const TABLE_HAS_API_SOURCE: ConstraintRule = (comp, ctx) => { if (comp.type !== 'Table') return []; const hasDataSource = comp.dataBindings?.some( b => ctx.dataSourceMap.get(b.sourceId)?.type === 'api' ); if (!hasDataSource) { return [{ path: comp.id, message: 'Table 组件必须绑定至少一个 API 类型数据源', severity: 'error', }]; } return []; };四、业务规则:从结构正确到逻辑合理
业务规则校验是三层校验体系的最上层。它不再关注结构合法性,而是判断业务逻辑是否合理。例如:订单总金额不能为负数、删除操作必须有二次确认、敏感数据的 API 必须走 POST 而非 GET。
AI 在这一层的价值是:从业务规则的自然语言描述中生成校验断言,或从历史配置数据中学习常见的逻辑错误模式。
/** * 业务规则校验示例:基于 DSL 描述的规则引擎 */ interface BusinessRule { id: string; description: string; /** 谓词:返回 true 表示校验通过 */ predicate: (config: PageConfig) => boolean; severity: 'error' | 'warning'; } const BUSINESS_RULES: BusinessRule[] = [ { id: 'REQ-DELETE-CONFIRM', description: '删除操作必须包含确认弹窗', predicate: (config) => { return config.interactions.every(i => { if (i.actions.some(a => a.type === 'apiCall' && a.payload.method === 'DELETE')) { // 检查该 interaction 是否关联了确认弹窗 return i.actions.some(a => a.type === 'openModal' && a.payload.modalType === 'confirm'); } return true; }); }, severity: 'error', }, { id: 'WARN-LARGE-FORM', description: '表单字段超过 20 个时建议分步', predicate: (config) => { const formComponents = config.components.filter(c => c.type === 'Form'); return formComponents.every(f => (f.children?.length ?? 0) <= 20); }, severity: 'warning', }, ];五、总结
低代码平台的校验体系需要三层递进:类型推导保证结构合法,组件约束保证关系完整,业务规则保证逻辑合理。每层的校验时机和处理策略不同——类型错误即时阻断、约束问题标记警告、业务规则违规阻止发布。AI 在当前阶段的合理角色是辅助规则生成和历史错误模式的分析,而非替代确定性的校验逻辑。