3. FHIR资源与RESTful API基础

FHIR 的互操作性建立在两个支柱之上:一是以“资源(Resource)”为最小交换单元的数据模型,二是以 RESTful 风格暴露这些资源的 API 约定。理解这两者的配合方式,是后续做集成、映射与事务处理的前提。

资源:FHIR 的数据原子

FHIR 把医疗信息切分为一组有明确边界的资源类型,例如 Patient(患者)、Observation(检验/观察)、Encounter(就诊)、MedicationRequest(用药医嘱)、Practitioner(医务人员)。每个资源实例都有统一的骨架字段:

  • resourceType:资源类型名,决定该实例遵循哪套结构定义。
  • id:在某个服务器内的逻辑标识,配合 resourceType 构成资源的唯一地址。
  • meta:元数据,含 versionId、lastUpdated、profile 等,用于版本控制与规范约束。
  • text:供人阅读的叙述性内容,便于非结构化展示。
  • 业务字段:如 Patient.name、Patient.birthDate、Observation.code、Observation.value[x]。

资源之间通过“引用(Reference)”建立关系,而不是嵌套复制。例如 Observation.subject 指向一个 Patient,Observation.encounter 指向一次 Encounter。引用通常写成 Patient/123 这样的相对形式,也可用绝对 URL。这种“引用而非内嵌”的设计让同一份患者数据只存一处,避免多副本不一致。

当标准字段不足以表达本地需求时,FHIR 提供“扩展(Extension)”。扩展以 extension 数组出现,每个扩展由 url 标识其定义,value[x] 承载取值。扩展必须由 StructureDefinition 约束,不能随意发明字段名,否则接收方无法解释。

RESTful API:对资源的统一操作

FHIR 把 HTTP 动词映射到资源操作,形成一套可预测的接口。以 Patient 为例:

操作HTTP 方法路径示例语义
读取GET/Patient/123取回指定患者
创建POST/Patient新建患者,服务器分配 id
更新PUT/Patient/123整体替换该患者
局部更新PATCH/Patient/123按补丁修改部分字段
删除DELETE/Patient/123删除或标记删除
搜索GET/Patient?name=张按条件查询集合

创建时,客户端 POST 一个不含 id 的资源,服务器返回 201 Created 并在 Location 头中给出新地址,如 Location: Patient/123/_history/1。更新时若带上 If-Match 头指定版本,可实现乐观锁,防止并发覆盖。

搜索参数:把查询变成可组合的条件

FHIR 搜索不是自由文本,而是基于预定义“搜索参数”。常见类型包括:

  • 字符串:name=张,默认前缀匹配、忽略大小写。
  • 令牌:code=http://loinc.org|1234-5,用于编码值,格式为 system|code。
  • 日期:date=ge2024-01-01,支持 ge、le、gt、lt 前缀。
  • 引用:subject=Patient/123,按引用目标过滤。
  • 数量:value=gt100,用于数值型观察。

多个参数之间是“与”关系,如 /Observation?patient=123&code=1234-5&date=ge2024-01-01 表示该患者、该检验项目、该日期之后的观察。返回结果是一个 Bundle,类型为 searchset,其中 entry 数组承载匹配资源,total 表示总数,link 中的 next 用于分页。

Bundle 与事务处理

Bundle 是 FHIR 的“容器资源”,可承载多个资源并表达它们之间的关系。常见类型有:searchset(搜索结果)、collection(集合)、transaction(事务)、batch(批处理)。其中 transaction 最关键:它要求服务器把包内所有操作作为一个原子单元执行,要么全部成功,要么全部回滚。

事务包中每个 entry 包含 request(方法、URL)和可选的 resource。若条目之间需要互相引用,可用 fullUrl 配合临时 id(如 urn:uuid:...)建立占位引用,服务器在提交后解析为真实地址。下面是一个事务请求的示例结构:

{
  "resourceType": "Bundle",
  "type": "transaction",
  "entry": [
    {
      "fullUrl": "urn:uuid:patient-1",
      "resource": { "resourceType": "Patient", "name": [{"family": "张"}] },
      "request": { "method": "POST", "url": "Patient" }
    },
    {
      "resource": {
        "resourceType": "Observation",
        "status": "final",
        "code": { "coding": [{"system": "http://loinc.org", "code": "1234-5"}] },
        "subject": { "reference": "urn:uuid:patient-1" },
        "valueQuantity": { "value": 5.6, "unit": "mmol/L" }
      },
      "request": { "method": "POST", "url": "Observation" }
    }
  ]
}

服务器处理成功后返回 200 OK 或 201 Created,响应 Bundle 中每个条目带 response.status 与 response.location,客户端据此获知新建资源的真实地址。若任一条目失败,整个事务回滚,响应中会指出失败条目及原因。

事务与批处理的区别:事务要求原子性,适合“一次提交一组强关联数据”;批处理不保证原子性,各条目独立成败,适合批量导入或非关键操作。
FHIR 资源、REST 操作与事务 Bundle 关系图 资源实例 Patient/123 Observation/456 Encounter/789 subject RESTful 操作 GET /Patient/123 POST /Patient PUT /Patient/123 PATCH /Patient/123 DELETE /Patient/123 GET /Patient?name=张 搜索参数 name=张 code=system|code date=ge2024-01-01 subject=Patient/123 value=gt100 _count=20 Bundle (type = transaction) entry[0] fullUrl: urn:uuid:patient-1 resource: Patient request: POST /Patient → 服务器分配 id=123 entry[1] resource: Observation subject: urn:uuid:patient-1 request: POST /Observation → 解析为 Patient/123 原子性 全成功或全回滚

把上述要素串起来看:资源定义“数据长什么样”,引用定义“资源之间怎么连”,扩展定义“标准之外怎么补”,搜索参数定义“怎么找”,CRUD 定义“怎么改”,Bundle 与事务定义“怎么把一组改动作为一个整体提交”。这六件事共同构成 FHIR 集成的最小知识闭环。

实践提醒:事务包中的临时 id 必须用 urn:uuid: 或 urn:oid: 形式,且同一包内唯一;服务器解析引用时按 fullUrl 匹配,不要用业务字段充当临时标识。