Skip to content

内容模型与扩展字段 ​

内容模型为站点、栏目与内容提供自定义结构化字段(EAV via JSONB):模型定义字段元数据,值存入宿主对象的 extend 列。政府站的「文号/发布机关」、游戏站的「评分/平台/发售日期」、产品站的「价格/规格」都由同一套机制承载;object / array / blocks 三类复合字段让「一篇文章多个小编组件」「动态区块编排」也不必新建表。

管理入口:/cms/models(权限 cms:model:*)。表:cms_models / cms_model_fields / cms_model_versions / cms_model_unique_values。普通操作员的列表、详情、字段、引用、版本和更新请求都带 siteId scope;平台管理员可以省略 scope 查看平台共享模型的全局引用。

字段类型 ​

共 17 种(CMS_FIELD_TYPES),编辑表单按类型渲染对应控件:

类型控件值形态
text / textarea单行 / 多行文本string
richtext富文本编辑器HTML string(写入前经 sanitizeCmsHtml)
number数字输入number
date / datetime日期 / 日期时间选择YYYY-MM-DD / YYYY-MM-DD HH:mm:ss
image / file媒体库选取 / 上传素材句柄(cms-res://{id})
select / radio下拉 / 单选选项 value
checkbox多选value[]
switch开关boolean
reference / references内容选择器(单选 / 多选)站内内容 id / id[]
object字段组(固定子字段)对象
array重复组件(同构子字段可增删排序)对象数组
blocks动态区块(按区块类型解释子字段)带 blockType 的对象数组

复合字段的子字段类型收敛为 CMS_COMPONENT_FIELD_TYPES(text / textarea / richtext / number / date / datetime / switch)——子字段内不再嵌套媒体与选项类字段。子字段同样可勾选「发布必填」,object 递归校验,array / blocks 逐项校验。

字段配置项 ​

模型编辑器中每个字段可配置:

配置作用
标识 name / 名称 labelname 为 extend 的 JSON key(^[a-z][a-z0-9_]*$),label 为表单与前台展示名
必填 required发布必填——见下方「校验分层」
检索 searchable值纳入全文检索(权重 C),并开放给 Headless API 的 extend.{字段} 过滤
列表显示 showInList字段值注入前台列表项(卡片角标场景,如评分/平台),后台内容列表同样展示
详情展示 showInDetail字段值注入前台详情页,由主题渲染(如公文「文件信息」表头)
详情分组 detailGroup / 排序 detailSort详情展示的分组标题与组内顺序;detailSort 按模型编辑器行序自动落库
默认值 defaultValue新建内容时自动填充(前端初始化 + 服务端创建兜底回填,双保险)
提示文案 placeholder编辑表单占位提示
选项来源 optionSource / 字典 dictCode / 选项 options见下方「选项来源」

「字段规则」(configuration)按类型展开,逐项都是写入校验的一部分:

规则适用语义
min / maxnumber数值闭区间
minLength / maxLength文本类字符长度区间;组件数组用它表示条数上限(缺省 1000)
unique标量字段模型内值唯一,由 cms_model_unique_values 落约束明细
referenceModelIdsreference / references允许引用的内容模型白名单
requiredWhen({ field, equals })全部条件必填:同模型某字段等于指定值时该字段必填
fieldsobject / array子字段定义
blockTypes({ code, label, fields })blocks允许的区块类型及其子字段

字段标识 name 在同一模型内必须唯一;编辑字段规则只产出模型工作稿,不会改变已发出内容的解释方式(见「模型版本」)。

选项来源(select / radio / checkbox) ​

来源配置方式适用
手工维护(默认)模型编辑器内直接填写,每行一个选项,格式 值|显示名(显示名可省略,如 pc|PC 或 PC)模型专属选项
引用系统字典只填字典编码,选项在读取模型时实时解析自 dict_items(仅启用项,按 sort 排序)多模型共用、需统一治理的选项

字典维护一处、所有引用它的模型字段自动同步。解析结果经 resolvedOptions 统一返回,内容编辑表单与前台翻译均按它渲染;字典编码不存在时解析为空数组,不影响其他字段。引用字典却未填编码在保存时即被拦截。

校验分层:草稿宽松、发布严格 ​

扩展字段的服务端校验(validateCmsStructuredFields)按目标状态分层,编辑器可以随时存草稿、不被半成品数据卡住:

时机校验强度
保存草稿 / 更新草稿宽松:仅校验类型合法性与选项 value 合法性,必填不拦截
提交审核 / 发布严格:额外校验 required、requiredWhen 与复合结构必填

无论哪一层,以下都在写入时强制:字段必须在该模型(或其固定版本)中定义、数值与长度区间、reference 必须指向存在的内容、referenceModelIds 白名单、blocks 区块类型必须在 blockTypes 中、select/radio/checkbox 的提交值必须命中已解析选项、unique 字段不重复。

前端编辑表单不挂 required 规则,改在字段 label 上标注「(发布必填)」提示;创建时 defaultValue 自动回填。校验统一挂在创建、更新、提审、发布全部写入口,Headless API 写入同样生效。

模型版本 ​

模型字段是可演进的配置,而内容必须能被确定性解释,因此两者分开:

  • 编辑模型(含字段规则)保存为模型工作稿(cms_models.publishedVersionId + hasUnpublishedChanges),列表与编辑器提示「存在未发布修改」
  • POST /api/cms/models/{id}/publish 校验工作稿后生成不可变模型版本(cms_model_versions,带 version、fields 快照与 contentHash)
  • 内容在创建 / 更新时把当时的模型版本固化进自己的修订快照(revision.snapshot.modelVersionId),渲染、列表翻译与校验一律按该版本取字段定义;模型后续改版只影响新稿,已审核修订保留原模型版本
  • GET /api/cms/models/{id}/versions 查看历史版本

已有工作稿与修订继续按其固化模型版本解释和校验扩展字段。模型发布新版本后,不会静默重解释已审核或已发布修订;内容要采用目标模型版本时,应预览字段映射和移除影响,再生成工作稿。

三级绑定 ​

模型通过宿主对象各自的 model_id + extend 列绑定到三级:

绑定级绑定位置值存放用途
站点cms_sites.model_idcms_sites.extend站点级运营元数据(备案号、客服电话等),主题经 site.extend 读取
栏目cms_channels.model_id新建内容工作稿的 extend创建内容未指定模型时提供默认模型
内容工作稿快照及发布后的 cms_contents.model_id工作稿快照及发布后的 cms_contents.extend内容独立绑定模型,编辑与展示按其固定模型版本解释;移动栏目保留模型

内容模型切换走内容管线 → 编辑协作中的「切换内容模型」:先预览字段映射和移除影响,确认后写入新工作稿。图文 / 图集 / 音视频 / 外链属于内容形态,创建后不可修改。

站群归属治理 ​

模型带归属(cms_models.owner_site_id,创建后不可变更):

归属语义
平台共享(owner_site_id = null)全部站点可见、可绑定;内置模型(文章/产品)属此类;仅平台管理员可创建、更新或删除
站点专属仅归属站点可见、可绑定
  • 可见性过滤:模型列表与绑定下拉按「平台共享 + 当前站点专属」过滤,A 站的专属模型不会出现在 B 站的任何选择器里;普通请求缺少 siteId 会返回 400
  • 跨站绑定拦截:服务端在站点/栏目/内容绑定模型时校验归属(assertCmsModelUsableBySite),越权绑定直接 400
  • 引用清单:GET /api/cms/models/{id}/refs?siteId= 返回当前 scope 内模型被哪些站点/栏目/内容绑定;平台管理员省略 siteId 时返回全局引用,删除前应确认 channels、contentCount 和 siteExtendCount 均为 0
  • 后台模型列表展示「归属」列(平台共享 / 站点名徽章),创建时以单选指定,站点过滤器联动

前台渲染消费 ​

模型字段值由渲染管线翻译为展示值后注入主题上下文(CmsModelFieldValue[]):字典/选项 value 反查 label、checkbox 多值以「、」连接、日期格式化、switch 转 是/否、richtext 输出纯文本摘要(完整渲染由主题自行处理 rawValue)。reference / references / object / array / blocks 属于结构化取值,由主题读取 rawValue 自行渲染。

注入位置来源字段典型主题用法
ctx.content.modelFields(详情页)showInDetail 字段,按 detailGroup 分组、detailSort 排序gov-portal 用共享组件 ModelFieldTable 渲染公文「文件信息」双栏键值表;magazine 把 ratingField 拆出渲染大评分徽章、其余字段行内标签
item.modelFields(列表项)showInList 字段magazine / default 卡片角标(「9.5」「PC、PS5」chips)

列表场景按 modelId(或固定 modelVersionId)批量预载字段定义后同步翻译(一页列表通常仅涉及 1-2 个模型),首页区块、栏目列表、标签页与 Theme API 取数结果全部生效。主题侧消费方式见主题与模板开发。

质量检查 ​

编辑页的质量检查(GET /api/cms/editorial/{id}/quality)与模型切换预览共用同一份问题结构(cmsQualityIssueSchema:rule / severity / fieldPath / message)。界面将字段路径解析为中文名称,质量清单提供返回编辑字段的定位入口;它不阻断保存,发布时仍由服务端严格校验兜底。

规则级别检查内容
titleerror标题为空或仍是「未命名内容」
scheduleerrorexpireAt 不晚于 scheduledAt
dependencieserror依赖冻结(strict)失败:扩展字段与模型版本规则、素材引用、部件与栏目依赖等
summarywarning未填写摘要
coverwarning未选择封面
seoTitlewarning未设置独立 SEO 标题
accessibilitywarning正文图片缺少替代文本(定位到文档节点)

字段级问题来自 validateCmsStructuredFields:字段未在该模型 / 模型版本中定义、必填与条件必填缺失、数值与长度越界、组件条数超限、选项失效、blocks 区块类型未知、reference 指向不存在或不在白名单内的内容。

接口 ​

  • GET/POST /api/cms/models、GET/PUT/DELETE /api/cms/models/{id}(列表/详情/更新/删除的普通请求带 ?siteId=;列表返回平台共享 + 当前站点专属模型)
  • GET /api/cms/models/all?siteId= 栏目与内容模型下拉源
  • GET /api/cms/models/{id}/refs?siteId= 引用清单
  • GET /api/cms/models/{id}/versions?siteId= 不可变模型版本;POST /api/cms/models/{id}/publish 发布模型工作稿
  • 模型字段发布新版本后,服务端按模型归属为受影响站点提交重建任务(平台共享模型覆盖平台站点范围),静态页异步按新定义更新

Built with VitePress for local documentation preview.