字段类型与 Smart 组件

做一个「全字段对象」的录入页,手写时每个字段都是一摊活:选哪个控件、配什么校验、接哪个字典、要不要脱敏、reference 字段怎么做联想搜索……改一次模型,前端整排跟着改。这些不是业务逻辑,是同一套「字段该长什么样」的工程,在每个项目里被重写无数遍。

AuraBoot 把它收进声明:13 种数据类型描述数据语义,运行时映射到 50 个 Smart 组件,并按字典与字段特性自动推断控件。声明数据「是什么」,运行时给你对的控件、校验、字典、脱敏——同一个字段,出现在哪个页面都长一样。

这一页不空谈。我们有一个真实可导入的开源插件 com.auraboot.showcase(命名空间 sc),它用一个模型 showcase_all_fields、36 个字段,把整个字段武器库一次性摆出来——每一种数据类型、每一个有代表性的 Smart 组件、虚拟字段、字典、条件可见、状态机,全在里面,每个标识符都能 grep 到。

源码:plugins/showcase on GitHub ↗ · 导入一行命令:aura plugin publish plugins/showcase --yes 前置:Model 与 Field · DSL 引擎原理。本页所有类型/组件/计数来自 live 源码:DslRegistry.javaComponentRuntimeManifest.tsplugins/showcase/config

全字段展示列表 —— showcase 插件的列表渲染:顶部按状态分页签(全部/草稿/启用/审核中/已归档),状态与优先级带字典语义色,进度/评分以专用列渲染,全部由 DSL 声明

1. 全字段对象 —— 一个模型,把武器库摆出来

showcase_all_fields 一个模型 36 个字段,覆盖全部数据类型 + 17 种组件覆盖(renderComponent)+ 5 个字典 + 状态机 + 条件可见。它配了 list / form / detail 三种页面、2 个 dashboard(sc_arsenal_dashboard / sc_workflow_dashboard),还有一个子模型 sc_monthly_metric 驱动 monthly-grid 月度网格。下面按能力分组看真实配置(节选自 config/fields/showcase_all_fields.json)。

1.1 基础数据类型 —— 声明语义,平台建列 + 选控件

{ "code": "sc_name",     "dataType": "string",  "constraints": { "required": true, "maxLength": 200 },
  "feature": { "searchable": true, "sortable": true } },
{ "code": "sc_quantity", "dataType": "integer", "constraints": { "min": 0, "max": 99999 } },
{ "code": "sc_price",    "dataType": "decimal", "extension": { "precision": 14, "scale": 2 } },
{ "code": "sc_is_active","dataType": "boolean", "defaultValue": "true" },
{ "code": "sc_start_date","dataType": "date" },
{ "code": "sc_created_at","dataType": "datetime", "extension": { "readOnly": true } }

长度走 constraints.maxLength,没有 string(200) 内联语法;精度走 extension.precision/scale;只读走 extension.readOnly——都在字段上声明,页面继承。

1.2 枚举 + 字典 + 状态机

状态、优先级、分类都是 enum 绑字典(dictCode),自动用下拉并从字典加载选项与语义色:

{ "code": "sc_status",   "dataType": "enum", "dictCode": "sc_status_dict",   "defaultValue": "draft" },
{ "code": "sc_priority", "dataType": "enum", "dictCode": "sc_priority_dict" },
{ "code": "sc_category", "dataType": "enum", "dictCode": "sc_category_dict" }

sc_status 串起一条状态机 draft → active → review → archived,由命令驱动:sc:activate_showcasesc:submit_review_showcasesc:archive_showcase(状态流转命令,走命令管道带权限与审计)。这就是列表截图里「审核中 / 启用 / 已归档」徽章带不同颜色的来源。

1.3 组件覆盖(renderComponent)—— 同一个数据类型,换个控件

数据类型决定默认控件,extension.renderComponent 显式换控件。showcase 用到 17 种覆盖:

{ "code": "sc_progress", "dataType": "integer", "constraints": { "min": 0, "max": 100 },
  "extension": { "renderComponent": "progress" } },
{ "code": "sc_rating",   "dataType": "integer", "constraints": { "min": 0, "max": 5 },
  "extension": { "renderComponent": "rating" } },
{ "code": "sc_color",    "dataType": "string",  "extension": { "renderComponent": "colorpicker" } },
{ "code": "sc_tags",     "dataType": "string",  "extension": { "renderComponent": "multiselect" } },
{ "code": "sc_richtext_content", "dataType": "text", "extension": { "renderComponent": "richtext" } },
{ "code": "sc_budget",   "dataType": "decimal", "extension": { "renderComponent": "moneyinput", "currencySymbol": "¥" } },
{ "code": "sc_working_hours", "dataType": "string", "extension": { "renderComponent": "timerangepicker", "minuteStep": 15 } }

showcase 里出现的全部 renderComponent:progress · rating · colorpicker · multiselect · richtext · fileattachment · moneyinput · timepicker · daterange · timerangepicker · cascadeselect · treeselect · userselect · memberpicker · organizationselect · aifield · addressfield

1.4 选择器与关系字段

跨模型引用、组织/用户/成员选择、级联与树形——都是声明:

{ "code": "sc_owner_user", "dataType": "reference",
  "refTarget": { "targetModel": "sys_user", "targetField": "username" } },
{ "code": "sc_assignee",   "dataType": "string", "extension": { "renderComponent": "userselect" } },
{ "code": "sc_department", "dataType": "string", "extension": { "renderComponent": "organizationselect", "showHierarchy": true } },
{ "code": "sc_cascade_category", "dataType": "string",
  "extension": { "renderComponent": "cascadeselect", "levels": 3, "dictCode": "sc_cascade_category_dict" } }

reference 字段(sc_owner_user → sys_user)做跨模型联想选择;cascadeselect 三级级联绑字典 sc_cascade_category_dict

1.5 原生 file、AI 字段、条件可见

{ "code": "sc_attachment_file", "dataType": "file", "extension": { "multiple": true, "maxCount": 5 } },
{ "code": "sc_ai_summary", "dataType": "text",
  "extension": { "renderComponent": "aifield", "operation": "summarize",
    "sourceFields": ["sc_name", "sc_description", "sc_category"], "maxTokens": 300 } },
{ "code": "sc_advanced_settings", "dataType": "text",
  "description": "Only visible when sc_status === 'active'." }

file 是原生数据类型(上传 + 预览);aifield 声明「从哪些字段、做什么 AI 操作」;sc_advanced_settings 演示条件可见(visibleWhen,详见 交互与联动)。

一句话:showcase_all_fields 就是这一页所有概念的活样本。下面的「数据类型」「Smart 组件」两张表是词汇表,showcase 是「把词汇表实例化进一个对象」。

2. 13 种数据类型

数据类型(dataType)声明字段的存储与语义,平台据此建列、选默认控件、做格式化。

类型Java 映射DB 类型说明
stringStringVARCHAR短文本
textStringTEXT长文本
integerIntegerINTEGER整数
decimalBigDecimalDECIMAL精确小数
booleanBooleanBOOLEAN布尔
dateLocalDateDATE日期
datetimeLocalDateTimeTIMESTAMP日期时间
jsonObjectJSONBJSON 对象(也是 JSONB 虚拟字段的宿主列)
enumStringVARCHAR枚举(配合字典)
referenceLongBIGINT外键引用另一模型
computed虚拟计算字段(不建列)
ai_textStringTEXTAI 辅助文本
moneyDECIMAL + 币种多币种金额

3. 50 个 Smart 组件(完整清单)

数据类型决定默认控件,具体用哪个组件可由 renderComponent / component 指定。运行时注册表(ComponentRuntimeManifest.ts)目前注册 50 个组件——40 个通用 + 10 个由业务模块自注册,证明注册表是开放的。

表单输入(16)

input · textarea · select · multiselect · checkbox · radio · numberinput · switch · upload · richtext · formref · aifield · moneyinput · colorpicker · agenttoolpicker · guardrailseditor

日期时间(6)

datepicker · daterange · timepicker · timerangepicker · date · datetime

选择器 / Picker(7)

treeselect · cascadeselect · userselect · ownerselect · organizationselect · memberpicker · addressfield

展示(7)

display · imagedisplay · table · list · ratingfield · progressfield · fileattachment

交互(2)与布局(2)

button · navigation · form · layout

模块自注册(10)—— 注册表是开放的

业务模块按同一套契约注册自己的专用组件:决策模块 9 个(DecisionTableWorkbenchBlock / EventPolicyDesignerBlock / DecisionRuleBindingBlock / ExecutionLogTraceBlock 等)+ 报价模块 1 个(ProcessFeeRuleMatrixBlock)。新增组件 = 一个注册组件 + 一份配置 schema + 允许出现的上下文,与平台内建组件同等公民。

4. 组件自动推断 —— 多数时候你不用指定

绝大多数字段不必写 component:运行时按数据类型 + 字典 + 命名约定推断。

// RuntimeFieldRenderer.tsx — 组件解析
const componentName = useMemo(() => {
  if (field.dictCode && (!field.component || field.component === 'SmartInput')) {
    return 'SmartSelect';      // 绑了字典 → 自动用 Select
  }
  return field.component || 'SmartInput';  // 默认 SmartInput
}, [field.dictCode, field.component]);

名称解析优先级:精确匹配 → 小写匹配 → 加 Smart 前缀匹配 → 注册表兜底。所以 selectSelectSmartSelect 指向同一个组件。这就是为什么 showcase 里 sc_status(绑了 sc_status_dict)不写 renderComponent 也自动是下拉。

5. 虚拟字段 —— 不建列也能有字段

不是每个字段都需要一个物理列。三种虚拟字段:

类型持久化触发时机
只读计算(COMPUTED_READONLY)查询时 SpEL 计算total = qty * price
物化(MATERIALIZED)写入时计算并落库汇总金额
临时(TRANSIENT)页面内临时变量中间计算值

5.1 Roll-Up 汇总字段

父模型上声明,值由子模型记录聚合自动填充——「订单总额 = 所有未取消行项金额之和」不用写 service:

{
  "code": "or_total_amount",
  "dataType": "decimal",
  "feature": {
    "readonly": true,
    "rollUp": { "childModel": "order_line", "childField": "ol_amount",
      "childFk": "ol_order_id", "function": "SUM", "childFilter": "ol_status != 'cancelled'" },
    "precision": 10, "scale": 2
  }
}

支持 SUM / COUNT / AVG / MIN / MAX。子记录执行 CREATE/UPDATE/DELETE 命令时,命令管道自动重算父字段;另有批量重算 API POST /api/meta/rollup/recalculate。Roll-Up 字段自动只读。

5.2 JSONB 虚拟字段

把多个逻辑字段存进同一个 JSONB 物理列,不为每个加列——适合行业扩展 / 公司定制这类「字段多但不想动表结构」的场景:

{
  "code": "contact_phone",
  "dataType": "string",
  "extension": { "jsonbColumn": "extra_data", "jsonbPath": "contact_phone" }
}

宿主列须为 dataType: "json"。读写按类型自动转换(col->>'key'),UPDATE 用 JSONB || 合并保留未涉及的 key。关键:JSONB 虚拟字段 DSL 能力与物理字段完全一致——表单渲染、列表展示、16 种操作符筛选、排序、i18n、联动、条件显示、导出都支持。限制:jsonbPath 仅一级路径;feature.unique / feature.indexed 不生效(无物理列)。

6. 字段特性(FieldFeatureBean)—— 一处声明,渲染/校验/DB 全管

字段的行为不写在页面里,写在模型字段的 feature / constraints 上,页面继承(showcase 里 sc_namesearchable/sortablesc_quantitymin/max 就是):

分类特性
基础required readonly hidden disabled
查询searchable sortable filterable
导入导出exportable importable
数据库unique indexed primaryKey
默认与提示defaultValue placeholder helpText
长度length maxLength minLength
数值minValue maxValue precision scale
格式pattern(正则)

7. 数据字典与脱敏

7.1 字典系统

枚举/状态/来源这类有限取值走数据字典(DYNAMIC 键值 / TREE 层级)。showcase 用了 5 个字典:sc_status_dict · sc_priority_dict · sc_category_dict · sc_cascade_category_dict · sc_tree_dept_dict。字段绑字典后自动用 SmartSelect 并从字典加载选项;字典项 extra JSONB 可挂 color / icon / priority——这就是列表里「状态/优先级带语义色」的实现,不在页面硬编码颜色。

7.2 字段级脱敏

电话/证件这类敏感字段的脱敏是权限层能力,声明即生效,不在每个页面手写:hide(隐藏)/ partial(部分展示)/ hash(哈希)/ custom(自定义规则),由五层权限引擎在管道里统一求值。

8. 导入即可见

aura plugin publish plugins/showcase --yes

导入后菜单出现「字段展示 → 全字段类型」,list / form / detail 三页 + 2 个 dashboard 全部就绪(上方截图即 list 页)。这就是一个 config 型插件的全部表面积:模型 + 字段 + 字典 + 命令 + 页面,没有 controller、没有 service、没有 view 组件

典型错误

  • 数据类型写内联长度:string(200) 非法;长度放 constraints.maxLength
  • 该用字典却硬编码选项:状态/优先级走字典 + extra.color,别在页面写死 options 和颜色。
  • JSONB 虚拟字段想加唯一约束:无物理列,feature.unique / indexed 不生效;需要约束就物理化成真列。
  • 绕过 feature 在页面层补校验:required / pattern / 数值范围声明在模型字段,页面继承,别每页重写。

下一步