Content

最后更新:2026年7月3日

Content 是用 Content Type(模板)生成的一条实际数据。以服装电商为例,Content Type“商品”规定了商品名、价格、详细说明等条目的构成,而一件“Stainless Tumbler 500ml”就是遵循该模板的一条 Content

Content 由两部分组成。fields 中存放各字段的实际值,sys 中存放发布、版本、归档等状态。在 CMA 中,ContentSpace 的子资源,查询与删除路径以 /spaces/{spaceId}/contents 为准,创建与修改路径以 Content Type 子级的 /spaces/{spaceId}/content-types/{contentTypeId}/contents 为准。管理操作在 CMA 中执行,已发布的快照通过 CDA 进行传递。

资源结构

下面是已发布的 Content“Stainless Tumbler 500ml”的单条查询响应。除 sys(系统属性)外,还包含 fields(字段值)和 metadata(标签等附加信息)。

{
  "sys": {
    "id": "3trmXRM3RqbgSnifyg7PUl8DzDgDzP",
    "type": "Content",
    "space": { "sys": { "id": "HnQ32YiH", "type": "Refer", "targetType": "Space" } },
    "contentType": { "sys": { "id": "3trmXRLdJF4GBlAjtcuoZ7Pnxj8dlA", "type": "Refer", "targetType": "ContentType" } },
    "publish": {
      "version": 1,
      "at": "2026-06-18T09:51:44.128Z",
      "firstAt": "2026-06-18T09:51:44.128Z",
      "counter": 1,
      "by": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } }
    },
    "createdBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
    "createdAt": "2026-06-18T09:51:14.597Z",
    "updatedBy": { "sys": { "id": "3p4tcFbQRwz503VXdtHXNI5dZH5TVB", "type": "Refer", "targetType": "User" } },
    "updatedAt": "2026-06-18T09:51:44.128Z",
    "version": 2,
    "status": "Published"
  },
  "fields": {
    "price": { "en-US": 18000 },
    "productName": { "en-US": "Stainless Tumbler 500ml", "ko-KR": "스테인리스 텀블러 500ml" }
  },
  "metadata": { "tags": [] }
}

主要键:

  • sys.idContent 的唯一标识符。会填入单条查询、修改、删除、发布路径中的 {contentId}
  • sys.contentType:指向该 Content 所遵循的 Content TypeRefer
  • fields:各字段的值。键是字段的 apiName,值是按 Locale 划分的映射。详见下文 字段键就是 apiName
  • metadata.tags:为该 Content 添加的 Tag 列表。每一项的形态为 Refer<Tag>,若没有标签则为空数组 []

字段键就是 apiName

fields 对象的键是各字段的 apiName。它不是 Content Type 的内部 id,也不是内容工作室中显示的字段名(例如 商品名)。因此创建或读取 Content 时,必须用 Content Type 中定义的 apiName 作为键。

演示用“商品” Content Type 的字段及其 apiName 映射如下。

内容工作室名称apiNametypelocalizedrequired
商品名productNameShortTexttruetrue
价格priceLongfalsefalse
详细说明descriptionRichTexttruefalse
主图photoRefer<Media>falsefalse
品牌brandReferfalsefalse

每个字段值都是按 Locale 划分的映射。值的形态取决于 localized

  • localized: true 字段可以采用 { "<locale>": 值 } 的形态持有多个 Locale 值。例如:"productName": { "en-US": "Stainless Tumbler 500ml", "ko-KR": "스테인리스 텀블러 500ml" }
  • localized: false 字段(非本地化字段)只在 Space 默认 Locale 这一个键下填入值,不能填入其他 Locale 键。演示用 Space 的默认 Localeen-US,因此价格只持有 en-US 这一个键,如 "price": { "en-US": 18000 }

关于默认 Locale 是什么、required 字段需要填到哪些 Locale、以及无值时的 fallback 规则,请参见 多语言(概念)

各类型的值形态

填入 Locale 键内部的值的形态取决于字段的 type。大多数类型直接填入该类型的值(文本类型为字符串,NumberLong 为数字,Booleantrue/false)。由于形态是对象或数组而容易混淆的类型如下。

  • Location:是 { "latitude": <数字>, "longitude": <数字> } 对象。latitude 取值在 -90 到 90 之间,longitude 取值在 -180 到 180 之间,两个键都是必填。除已定义的键外不接受其他键,因此使用 latlnglon 这类缩写键会被拒绝。
  • Refer:值是指向目标的 Refer 对象,不能只填 id 字符串,而要直接填入 sys 对象。指向其他 ContenttargetTypeContent,指向 Media 时为 MediaRefer 形态本身在 系统属性 (sys) 的 Refer 形态 中定义。
  • Array:是装有元素类型值的 JSON 数组。元素为文本时是字符串数组,元素为 Refer(Content 或 Media)时是 Refer 对象的数组,而不是 id 字符串的数组。

下面是填入 fields 内部的值的示例(Space 默认 Localeen-US 的情况)。

{
  "location": { "en-US": { "latitude": 37.5662, "longitude": 126.9910 } },
  "tags": { "en-US": ["新品", "限量版"] },
  "photo": { "en-US": { "sys": { "type": "Refer", "id": "3trmXRM3RqbgSnifyg7PUl8DzDgDzP", "targetType": "Media" } } },
  "photos": { "en-US": [ { "sys": { "type": "Refer", "id": "3trmXRM3RqbgSnifyg7PUl8DzDgDzP", "targetType": "Media" } } ] }
}

系统属性 (sys)

每个 Content 都将公共系统属性存放在 sys 对象中。spacecontentTypecreatedByupdatedByRefer 形态({ "sys": { "id", "type": "Refer", "targetType" } })填入。

属性类型说明
idstring资源唯一标识符。
typestring资源种类。Content 始终为 "Content"
spaceRefer<Space>Content 所属的 Space
contentTypeRefer<ContentType>Content 所遵循的 Content Type
publishobject发布状态指针。参见下方各键。
archiveobject归档信息。仅在归档时存在,否则没有该键。参见下方各键。
createdByRefer<User>创建该资源的用户。
createdAtstring (date-time)创建时间。
updatedByRefer<User>最后修改的用户。
updatedAtstring (date-time)最后修改时间。
versioninteger (≥1)资源版本。每次创建、修改、发布、取消发布、归档等变更都会加 1。
statusstring (enum)发布状态。为下列 4 种之一。

status 为下列 4 种之一。

status含义
Draft正在编写、尚未发布的状态。
Changed曾经发布过,但之后被修改、尚有未发布变更的状态。
Published已发布且没有未发布变更的状态。
Archived已归档的状态。

publish 对象是指向发布状态的指针。处于已发布时拥有以下全部键。

类型说明
versioninteger发布时的 sys.version
atstring (date-time)最后发布时间。
firstAtstring (date-time)首次发布时间。即使取消发布也会保留。
counterinteger累计发布次数。
byRefer<User>最后发布的用户。

取消发布后,publish 中的 versionatby 会被去除,只保留 firstAtcounter。若从未发布过,则 publish{ "counter": 0 }

archive 对象仅在归档时存在。归档时拥有 version(归档时的 sys.version)、at(归档时间)、by(归档的用户);非归档状态时则没有 archive 键本身。

下方示例中的 sys.version 与所有时间值都是实际调用时刻的值,每次调用都会不同。

发布、版本与并发

Content 的生命周期如下。

  • 创建后 statusDraftContent 在创建时不会自动发布。这一点与 Content Type 不同。Content Type 在创建的同时会自动发布,而 Content 必须经过单独的发布调用才会进入传递路径。
  • 发布后 status 变为 Published
  • 发布后再修改,status 会变为 Changed,表示存在未发布的变更。
  • 取消发布后 status 会再次变回 Draft
  • 要归档必须先取消发布。处于已发布状态的 Content 无法直接归档。

sys.version 每次变更都会加 1。

修改、部分修改、发布、取消发布、归档、取消归档的请求都必须在 x-weegloo-version 头中携带当前的 sys.version。若该值缺失或与当前版本不一致,将被视为并发修改冲突,请求会被拒绝。创建与删除请求不带此头。发布、取消发布、归档、取消归档等状态转换没有单独的请求体。

API

下面所有端点的基准 URL 均为 https://cma.weegloo.com/v1,并且 Authorization 头需要携带用于认证 CMA 的 Bearer 令牌。修改、部分修改、发布、取消发布、归档、取消归档为实现乐观并发控制,必须同时发送 X-Weegloo-Version 头(当前资源的 sys.version)。

fields.*/contents 列表(GET /spaces/{spaceId}/contents)进行过滤或排序时,必须同时以 sys.contentType.sys.id={contentTypeId} 指定 Content TypecontentType={contentTypeId} 这种形式无法替代。/content-types/{contentTypeId}/contents 已在路径中明示了 Content Type,因此无需另行指定。

  • Content Type:该 Content 的模板(字段定义、apiName)。
  • CDA Content:将已发布的 Content 传递给访客(读取)。
  • Tag:添加到 metadata.tags 的分类标签。
  • 多语言(概念):默认 Locale、必填填充、fallback。