字段类型与 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.java、ComponentRuntimeManifest.ts、plugins/showcase/config。

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_showcase、sc:submit_review_showcase、sc: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 类型 | 说明 |
|---|---|---|---|
string | String | VARCHAR | 短文本 |
text | String | TEXT | 长文本 |
integer | Integer | INTEGER | 整数 |
decimal | BigDecimal | DECIMAL | 精确小数 |
boolean | Boolean | BOOLEAN | 布尔 |
date | LocalDate | DATE | 日期 |
datetime | LocalDateTime | TIMESTAMP | 日期时间 |
json | Object | JSONB | JSON 对象(也是 JSONB 虚拟字段的宿主列) |
enum | String | VARCHAR | 枚举(配合字典) |
reference | Long | BIGINT | 外键引用另一模型 |
computed | — | — | 虚拟计算字段(不建列) |
ai_text | String | TEXT | AI 辅助文本 |
money | — | DECIMAL + 币种 | 多币种金额 |
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 前缀匹配 → 注册表兜底。所以 select、Select、SmartSelect 指向同一个组件。这就是为什么 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_name 的 searchable/sortable、sc_quantity 的 min/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 / 数值范围声明在模型字段,页面继承,别每页重写。
下一步
- DSL 交互与联动 —— 让这些字段动起来:联动与 action
- DSL 能力矩阵 —— 回到全景
- Model 与 Field · showcase 插件源码 ↗