跳到主要内容

NocoBase Meta V2 草案与首批迁移清单

1. 背景

当前 RGB 的 NocoBase 方案,已经具备了 SQLAlchemy meta -> admin_server metadata -> NocoBase sync 的基础链路,但离还原 bo/admin 的显示和编辑体验还有一个明显缺口:

  • FieldInfo.type 只有 10 个粗类型:string/int/decimal/bool/datetime/enum/color/date/text/array
  • admin_server 暴露给前端的字段元信息也只覆盖 type/editable/ref/enum_options/sensitive
  • sync_meta.py 当前仍然把:
    • text -> Input.TextArea
    • array -> Input.JSON
  • 老后台实际使用了图片预览、链接跳转、时间戳、富文本、颜色、金额、多选等大量语义化组件

这导致当前 meta 更像“数据库字段类型映射”,而不是“显示层字段语义”。

NocoBase 官方文档本身是支持这类增强的,尤其是:

结论很明确:下一步不应该继续只扩 type,而应该把“底层值类型”和“显示/编辑语义”拆开。

2. 当前代码里最核心的缺口

相关文件:

  • servers_v2/shared/rgb_db/src/rgb_db/models/meta.py
  • servers/admin_server/app/metadata/types.py
  • servers/admin_server/app/metadata/registry.py
  • tools/nocobase/sync_meta.py
  • tools/nocobase/plugin-rgb-preset-actions/dist/client/index.js

2.1 现在的 type 表达力不够

以下场景目前都被压成了 stringtext

  • 图片 URL
  • 外链 URL
  • 站内路径
  • 裸域名
  • 邮箱
  • 富文本 HTML
  • 普通多行备注
  • Markdown

这会带来两个后果:

  • NocoBase 无法自动选出最贴近老后台的字段界面
  • 下游消费方只能继续靠硬编码补丁识别字段语义

2.2 文档、同步代码、旧后台三者已经漂移

当前仓库里同时存在三种认知:

  • 本地文档认为 text -> Markdown
  • 同步代码实际是 text -> TextArea
  • 老后台 EditText 用的是 CKEditor HTML,不是 Markdown

这说明 text 至少应该拆成:

  • plain_text
  • richtext_html
  • markdown

2.3 下游消费方已经在偷偷使用另一套字段词汇

plugin-rgb-preset-actions 客户端渲染器当前识别的是:

  • text
  • json
  • int
  • float
  • bool
  • select
  • color
  • date
  • timestamp

这套词汇与 SQLAlchemy FieldInfo.type 并不一致,说明继续把所有语义都挤进一个 type 会越来越难维护。

3. 建议的 FieldInfo V2 结构

推荐思路:保留“值类型”,新增“UI 语义”和“显示提示”。

3.1 建议保留的 primitive type

PrimitiveType = Literal[
"string",
"int",
"decimal",
"bool",
"date",
"datetime",
"text",
"array",
"json",
]

3.2 建议新增的 ui_kind

UiKind = Literal[
"default",
"url",
"attachment_url",
"internal_path",
"domain",
"email",
"phone",
"richtext_html",
"markdown",
"unix_timestamp",
"money",
"percent",
"odds",
"m2m_array",
"multi_select",
]

3.3 建议新增的字段属性

@dataclass(frozen=True)
class FieldInfo:
type: PrimitiveType
label: dict[str, str] = field(default_factory=dict)
editable: bool = False
bulk_editable: bool = False
display: bool = True
sensitive: bool = False
mask_type: Literal["password", "phone", "bank_num"] | None = None
ref: RefInfo | None = None
enum_options: dict[int, dict[str, str]] | None = None

# v2 additions
ui_kind: UiKind = "default"
copyable: bool = False
preview: Literal["none", "link", "image", "download"] = "none"
timezone: str | None = None
upload_strategy: Literal["none", "base64_image", "attachment", "external_url"] = "none"
validator: Literal["none", "internal_path", "domain", "url"] = "none"
format_hint: str | None = None

3.4 为什么不建议继续只扩 type

因为下面这些字段虽然底层都是 string,但显示和编辑语义完全不同:

  • banner.jump_url
  • banner.jump_path
  • player_notice.outsite_url
  • player_notice.insite_url
  • banner.img_url
  • download.url
  • player_level.domain

它们不该继续共用同一个 type="string" 再靠调用方猜。

4. NocoBase 映射建议

4.1 primitive type -> 默认界面

保持当前思路,但需注意 NocoBase 侧的 type 精度:

RGB primitiveNocoBase typeNocoBase interfacex-component注意
stringstringinputInput
intbigInt(RGB 的 BIGINT 列)或 integerintegerInputNumberMySQL BIGINT 必须用 bigInt,否则 JS 精度丢失
decimaldecimalnumberInputNumber
boolbooleancheckboxCheckbox
datedateOnlydateOnlyDatePicker不应映射到 datetimeNoTz,那是带时间的
datetimedatetimeNoTzdatetimeNoTzDatePickershowTime: true
texttexttextareaInput.TextArea不是 Markdown
jsonjsonjsonInput.JSON

4.2 ui_kind -> 覆盖/增强规则

实现状态说明:下表中标 ✅ 的 ui_kind,NocoBase 已有原生 interface/plugin 支持。sync_meta.py 已通过 UI_KIND_OVERRIDE 字典实现第一批原生映射(url、email、phone、percent、unix_timestamp、multi_select),这些 ui_kind 现在直接输出对应的原生 interface + x-component。标 🔧 的为官方无直接对应、仍走 TYPE_MAP 默认值 + rgbMeta hint 供 RGBPrettyField 自定义渲染。注意:即使使用了原生 interface,rgbMeta 仍然会写入 options,确保页面层 RGBPrettyField 在需要时仍能读到 hint。

ui_kind推荐 NocoBase 能力说明原生支持
urlURL field(interface: url, x-component: Input.URL用于外部链接
attachment_urlAttachment (URL) ⚠️ 付费插件 @nocobase/plugin-field-attachment-url(Community Edition+);未启用时可用 JS Field 或 RGBPrettyField 替代只读预览用于图片/文件预览
internal_pathString + validator + copyable(需 Text Copy 插件)用于 /event/123 这类站内路径🔧
domainString + validator + copyable(需 Text Copy 插件)用于裸域名🔧
emailEmail field(interface: email邮件语义
phonePhone field(interface: phone手机号语义
richtext_html只读:JS Column 渲染 HTML;编辑:Rich text(需确认与 CKEditor HTML 兼容性)或 JS Field保留 HTML 内容🔧 部分
markdownMarkdown(Vditor)(插件 @nocobase/plugin-field-markdown-vditor,内置但默认不启用)真正使用 Markdown 时再启用✅ 插件
unix_timestampUnix timestamp field(interface: unixTimestamp表格和表单都按时间处理
moneyNumber + money formatter / JS Column数值展示语义🔧
percentPercent field(interface: percent百分比展示
oddsNumber + odds formatter / JS Column体育赔率展示🔧
m2m_arrayMany-to-Many(Array)(interface: m2m代替 JSON textarea
multi_selectMultiple Select(interface: multipleSelect用于纯枚举多选

4.3 一个关键原则

如果旧后台是“读写协议特殊”,只改字段类型还不够。

典型例子:

  • 老后台编辑横幅、弹窗、推广时,提交的是 img_b64
  • 数据库存的是 img_url

这类字段在 NocoBase 里可以先恢复“显示预览”,但要恢复“编辑上传”,必须同时补:

  • 上传接口
  • 上传后回填 URL
  • 或自定义 JS Field / action workflow

5. 首批字段迁移清单

本节按三类拆:

  • A 类:可直接改 meta,风险低
  • B 类:需要接口/上传协议配合
  • C 类:先做显示增强,不急着改底层写入

5.1 A 类:可直接改 meta

字段当前目标说明
download.urlstringurl下载链接
coin_transaction.web_urlstringurl外链
player_notice.outsite_urlstringurl站外跳转
banner.jump_urlstringurl外链跳转
coin_transaction.linkstringurl外部链接
daebak_email.emailstringemail邮件语义
*_ts / ts / start_ts / end_ts / expire_ts / settle_ts / event_ts / block_tsintunix_timestamp全局批量规则
*_percent / percent / percentagedecimal/intpercent百分比展示
oddsdecimalodds体育赔率展示
phone_numstringphone可结合 mask
wallet_addressstringdefault + copyable钱包地址适合复制
ip / register_ip / last_login_ip / user_ipstringdefault + copyable便于排查

5.2 B 类:需要接口/上传协议配合

字段当前目标说明
banner.img_urlstringattachment_url旧后台编辑用 img_b64
player_notice.img_urlstringattachment_url旧后台编辑用 img_b64
promotion.imgstringattachment_url旧后台编辑用图片上传
player_level.icon_urlstringattachment_url旧后台编辑可改图标
game.icon_urlstringattachment_url若未来要在 NocoBase 编辑图标,需补上传
provider.entrance_icon_urlstringattachment_url若需编辑则要补上传
provider.category_icon_urlstringattachment_url若需编辑则要补上传
promotion.contenttextrichtext_html旧后台是 CKEditor HTML
agent_notice.contenttextrichtext_html旧后台是富文本弹窗内容
agent_announcement.contenttextrichtext_html跑马灯/公告内容
player_message.contenttextrichtext_html站内信内容

5.3 C 类:先做显示增强

字段当前目标说明
banner.jump_pathstringinternal_path不要误判成 URL
player_notice.insite_urlstringinternal_path站内路由
agent_domain.domainstringdomain裸域名
player_level.domainstringdomain主域名
player.register_domainstringdomain注册域名
player.last_login_domainstringdomain登录域名
player_message.tagsarraym2m_array不该是 JSON 文本框
player_message.levelsarraym2m_array不该是 JSON 文本框
player_message.groupsarraym2m_array不该是 JSON 文本框
attendance_event.levelsarraym2m_array多选关系
deposit_event.groupsarraym2m_array多选关系
deposit_event.levelsarraym2m_array多选关系
deposit_event.weeksarraymulti_select星期多选
bank_stat_day.deposit_amountstringmoney旧后台按 money 列展示
bank_stat_day.withdraw_amountstringmoney旧后台按 money 列展示
coin_transaction.ratestringdecimal/money需要确认是否可改底层类型

6. 具体业务页面与字段语义映射

6.1 图片预览

老后台组件:

  • bo/admin/src/ui/components/ElTable/ElTableColumnImg.vue
  • bo/admin/src/ui/components/ElForm/ElFormItemImage.vue
  • bo/admin/src/ui/views/desktop/GameProvider/GameProviderView.vue

对应 meta 建议:

  • ui_kind = "attachment_url"
  • preview = "image"

6.2 链接跳转

老后台组件:

  • bo/admin/src/ui/components/ElTable/ElTableColumnLink.vue

对应 meta 建议:

  • 外链:ui_kind = "url"
  • 站内路径:ui_kind = "internal_path"
  • 域名:ui_kind = "domain"

6.3 时间戳

老后台组件:

  • bo/admin/src/ui/components/ElTable/ElTableColumnTs.vue
  • bo/admin/src/ui/components/ElForm/ElFormItemDateTime.vue

对应 meta 建议:

  • ui_kind = "unix_timestamp"
  • timezone = "Asia/Seoul" 或在显示层统一配置时区

6.4 富文本

老后台组件:

  • bo/admin/src/ui/components/EditText.vue
  • bo/admin/src/ui/views/desktop/Promotion/InnerAdd.vue
  • bo/admin/src/ui/views/desktop/Web/Content/index.vue

对应 meta 建议:

  • HTML 内容:ui_kind = "richtext_html"
  • 普通备注:保持 text + plain textarea
  • Markdown:只在业务明确转 Markdown 后启用

7. 建议的落地顺序

Phase 1: 先扩 meta 结构,不动业务接口

目标:

  • FieldInfo / FieldMeta / metadata JSON 里加 ui_kind 等字段
  • 不立即修改现有 PATCH/create 协议
  • 先让 NocoBase 能准确显示

涉及文件:

  • servers_v2/shared/rgb_db/src/rgb_db/models/meta.py
  • servers/admin_server/app/metadata/types.py
  • servers/admin_server/app/metadata/scanner.py
  • servers/admin_server/app/metadata/registry.py

Phase 2: 升级 sync_meta 映射器

目标:

  • type 改为 type + ui_kind
  • array 不再默认变 Input.JSON
  • text 不再一刀切
  • unix_timestamp / attachment_url / url 等开始落到 NocoBase

涉及文件:

  • tools/nocobase/sync_meta.py

Phase 3: 升级 NocoBase 侧消费方

目标:

  • plugin-rgb-preset-actions 表单渲染器识别 ui_kind
  • plugin-rgb-ai-builder / 页面生成器输出更合理的字段展示
  • 图片上传、富文本编辑、数组选择等场景再逐步替换为定制化 UI

涉及文件:

  • tools/nocobase/plugin-rgb-preset-actions/dist/client/index.js
  • tools/nocobase/plugin-rgb-ai-builder/dist/server/index.js
  • tools/nocobase/gen_pages.py

8. 推荐的首批实施范围

如果只做一轮最划算的改造,建议顺序是:

  1. unix_timestamp
  2. url
  3. attachment_url
  4. internal_path/domain
  5. m2m_array
  6. richtext_html
  7. money/percent/odds

原因:

  • 这几类字段最直接影响 NocoBase 的可用性
  • bo/admin 的体感差异最大
  • 官方文档已有对应能力,落地阻力最小

9. 本文档的判断边界

本文是静态梳理结果,未连生产/测试 NocoBase 实例做在线校验。

因此以下内容仍需二次确认:

  • Attachment(URL)@nocobase/plugin-field-attachment-url 是 Community Edition+ 付费插件(内置但默认不启用)。需确认当前 NocoBase 许可证是否已覆盖。若未覆盖,图片预览走 JS Field/RGBPrettyField 替代,编辑上传走 RGBPatchAction 自定义组件(参见 guide 中方案 B)
  • richtext_html 的读写分层
    • 只读展示:推荐 JS Column(直接渲染 HTML),或 RGBPrettyField(以纯文本预览)
    • 编辑模式:NocoBase 的 Rich Text 组件底层是 TinyMCE/Quill,输出格式可能与老后台 CKEditor 的 HTML 不完全兼容,需在测试环境做双向验证(CKEditor HTML → Rich Text 加载 → 保存后 HTML 是否变形)
    • 如果兼容性不好,编辑模式可走 JS Field 嵌入 CKEditor CDN
  • money / rate 这类字段是否允许同步修正数据库底层类型
  • 时区最终统一使用 KST、UTC 还是按前端 locale 渲染

10. 实现优先级建议

基于"官方原生能力优先、自定义 rgbMeta 兜底"的原则:

第一批(直接切原生 interface,sync_meta.py TYPE_MAP 改动):

  • url → interface: url
  • email → interface: email
  • phone → interface: phone
  • percent → interface: percent
  • unix_timestamp → interface: unixTimestamp
  • multi_select → interface: multipleSelect

第二批(需启用插件或验证兼容性):

  • markdown → 启用 Markdown(Vditor) 插件
  • attachment_url → 启用 Attachment(URL) 插件,或走 RGBPrettyField + RGBPatchAction
  • copyable → 启用 Text Copy 插件

第三批(官方无直接对应,保留 rgbMeta + 自定义渲染):

  • internal_pathdomainmoneyoddsrichtext_htmlm2m_array

注意:切换原生 interface 后,已生成的页面 schema 中写死的 RGBPrettyField 节点不会自动切换,需同步更新 gen_pages/rgb-builder 的字段判断逻辑(有原生 interface 时不再强制 RGBPrettyField)。