03 · CRUD 與搜尋規格

3.1 基本約定

項目 值
Base URL https://{host}/fhir/r4
Content-Type / Accept application/fhir+json
能力宣告 GET /metadata → capabilitystatement.json
字元編碼 UTF-8
未帶 Accept 一律回 application/fhir+json(不做內容協商猜測)

3.2 互動矩陣

C=create、R=read、V=vread、U=update、P=patch、D=delete、H=history、S=search。

資源 C R V U P D H S 備註
Patient ✓ ✓ ✓ ✓ HIS 唯讀
Encounter ✓ ✓ ✓ ✓ HIS 唯讀
EpisodeOfCare ✓ ✓ ✓ ✓ ✓ ✓
Coverage ✓ ✓ HIS 唯讀
Practitioner / PractitionerRole ✓ ✓ HIS 唯讀
RelatedPerson ✓ ✓ ✓ ✓ ✓ ✓ 同意書用
CareTeam ✓ ✓ ✓ ✓ ✓ ✓
Condition ✓ ✓ ✓ ✓ ✓ ✓
Goal ✓ ✓ ✓ ✓ ✓ ✓
CarePlan ✓ ✓ ✓ ✓ ✓ 更新=新建+replaces
AllergyIntolerance ✓ ✓ HIS 唯讀
MedicationRequest ✓ ✓ ✓ ✓ ✓ ✓ ✓ 簽署後限縮,見 3.4
MedicationAdministration ✓ ✓ ✓ ✓ ✓ 建立後不可改
ServiceRequest ✓ ✓ ✓ ✓ ✓ ✓ ✓
Procedure ✓ ✓ ✓ ✓ ✓ 建立後不可改
Specimen ✓ ✓ HIS 唯讀
DiagnosticReport ✓ ✓ ✓ ✓ HIS 唯讀
Observation ✓ ✓ ✓ ✓ ✓ 建立後不可改
Composition ✓ ✓ ✓ ✓ ✓ ✓ 已簽後限縮,見 3.4
Task ✓ ✓ ✓ ✓ ✓ ✓ ✓
Communication ✓ ✓ ✓ ✓ ✓ 建立後不可改
Consent ✓ ✓ ✓ ✓ ✓ ✓
Provenance ✓ ✓ ✓ 僅新增,永不修改
AuditEvent ✓ ✓ 伺服器產生

3.2.1 沒有 delete

任何資源都不提供 DELETE。 誤建的資料以 status = entered-in-error 標記:

PATCH /MedicationRequest/8f3a HTTP/1.1
Content-Type: application/json-patch+json
If-Match: W/"3"

[{ "op": "replace", "path": "/status", "value": "entered-in-error" }]

伺服器同時自動產生 AuditEvent(action=D)。標為 entered-in-error 的資源仍可 read,但預設不出現在 search 結果(除非帶 status=entered-in-error)。

3.2.2 建立後不可改的資源

MedicationAdministration、Procedure、Observation、Communication 一旦建立就是已發生事實的紀錄,只能標 entered-in-error 後重建,不能 update。這對應原型「執行紀錄不可回頭編修」的行為。

3.3 版本並行控制

所有可寫資源強制樂觀鎖。

GET /Composition/7c21
→ 200 OK
  ETag: W/"4"
  Last-Modified: Sun, 31 Aug 2026 09:12:04 GMT
PUT /Composition/7c21
If-Match: W/"4"
狀況 回應
版本相符 200 OK,ETag: W/"5"
版本落後 409 Conflict + OperationOutcome(code=conflict)
缺 If-Match 412 Precondition Failed

原型的 store 已有等價檢查(「其他分頁已有較新版本;請保留畫面內容並重新整理」)。API 層的要求更嚴:沒帶 If-Match 就直接拒絕,不允許盲目覆寫。

409 的 OperationOutcome 必須帶目前版本,讓客戶端能就地合併:

{
  "resourceType": "OperationOutcome",
  "issue": [{
    "severity": "error", "code": "conflict",
    "diagnostics": "資源已被其他工作階段更新至 versionId=6。請重新讀取後合併,內容未被覆寫。",
    "details": { "text": "currentVersionId=6" }
  }]
}

3.4 狀態相依的可寫性

update 是否合法取決於資源當下狀態,不只取決於權限。

MedicationRequest

狀態 可改 不可改
draft(未簽) 全部欄位 —
已簽、未送出 status(→cancelled) 藥品、劑量、途徑、頻次
已送出 HIS 無 全部。改動須走取消+重新開立
active(HIS 已接受) status(→stopped) 其餘

已送出的醫囑要改內容,必須取消再重開,不能就地 update——因為 HIS 端的 hisPrescriptionNumber 已經產生,就地改會讓兩端不一致(BR-ORD-006)。

Composition

狀態 可改
preliminary(草稿) 全部欄位
final(已簽) 無。更正一律新建版本 + relatesTo[replaces]
amended 無

3.5 條件式互動

互動 支援 用途
條件式建立(If-None-Exist) ✓ 劑次唯一性、HIS 事件冪等
條件式更新(PUT /X?params) ✗ 語義模糊,一律禁用
條件式刪除 ✗ 沒有 delete

劑次唯一性的實作:

POST /MedicationAdministration
If-None-Exist: identifier=http://icu.emr.local/occurrence-id|M1-V1-D1&status=completed

已存在同劑次的完成紀錄時回 200 OK(回傳既有資源)而非 201,客戶端據此得知「本劑次已有完成紀錄,不可重複給藥」。這比先查再寫安全:不會有兩個護理師同時通過檢查的競態。

3.6 搜尋

3.6.1 共用參數

_id、_lastUpdated、_sort、_count(預設 20,上限 200)、_include、_revinclude、_summary、_elements、_total=accurate。

分頁走標準 Bundle.link(self / next / previous / first / last)。客戶端不得自行拼 offset,一律跟隨 next 的 URL。

3.6.2 各資源搜尋參數

資源 支援參數
Patient identifier, name, birthdate, _has:CareTeam:participant:participant
Encounter patient, status, date, identifier, episode-of-care
EpisodeOfCare patient, status, date
CareTeam patient, participant, status, encounter
Condition patient, encounter, code, verification-status, recorded-date
Goal patient, lifecycle-status, target-date
CarePlan patient, status, date, replaces
MedicationRequest patient, encounter, status, intent, authoredon, requester, identifier, code, _has:MedicationAdministration:request:status
MedicationAdministration patient, encounter, request, status, effective-time, performer, identifier
ServiceRequest patient, encounter, status, category, code, authored, requester, performer, identifier, priority
Procedure patient, encounter, based-on, status, date, performer
DiagnosticReport patient, encounter, status, category, code, issued, based-on, specimen, identifier
Observation patient, encounter, code, date, status, category, component-code, part-of, derived-from, performer
Composition patient, encounter, type, status, date, author, related-id(relatesTo), section-code(本地)
Task patient, encounter, status, owner, focus, business-status, authored-on
Communication patient, encounter, category, sent, sender, recipient
Provenance target, agent, recorded, activity(本地), entity
Consent patient, status, category, date
AuditEvent patient, agent, date, action, entity, outcome

3.6.3 時間窗

原型的三種時間窗直接對映到日期區間搜尋:

時間窗 查詢
24 小時 date=ge2026-08-30T19:00:00+07:00&date=le2026-08-31T19:00:00+07:00
本班起訖 由客戶端依 PractitionerRole 的班別起訖換算後帶入相同格式
全部 不帶 date

伺服器不提供 window=shift 這種語意參數——班別起訖屬於排班資料,換算責任在客戶端,避免伺服器對「本班」有隱含定義。

3.6.4 一次載入病人工作區

原型的病人詳細頁需要多種資源。用 _include / _revinclude 減少往返:

GET /MedicationRequest?patient=Patient/abc&status=active
    &_revinclude=MedicationAdministration:request
    &_include=MedicationRequest:requester
    &_count=100
GET /DiagnosticReport?patient=Patient/abc&date=ge2026-08-30
    &_include=DiagnosticReport:result
    &_include=DiagnosticReport:specimen
    &_revinclude=Provenance:target

第二個查詢一次帶回報告、逐項數值、檢體、以及已閱/判讀的 Provenance——正好是 S13 檢驗結果頁的完整資料需求。

3.6.5 搜尋不做的事

GET /ValueSet/his-medication/$expand?filter=paracetamol&count=20&offset=0

3.7 Bundle

transaction(原子)

以下情境必須用 type=transaction,全成功或全失敗:

情境 內容
一次開立多項醫囑 N × MedicationRequest/ServiceRequest,共用同一 extension[orderSet]
簽署文件 Composition 更新 + Provenance 建立
接收交班 Provenance 建立 + N × Task.owner 更新
病歷更正 新 Composition 建立 + 舊版 status 更新

原型的 catalogOrder 一次可建多筆醫囑,但每筆之後仍各自獨立簽署、送出、執行、回報。transaction 只保證「建立」這一步的原子性,不代表後續生命週期綁在一起。

batch(非原子)

只用於純讀取的批次載入。任何寫入不得用 batch。

3.8 自訂操作

只在「多資源副作用必須原子」時才定義操作,其餘一律用標準互動。

操作 方法 說明
GET /Patient/$roster GET 回傳目前登入者的照護名單(Bundle,含 Patient+CareTeam+作用中醫囑計數)。取代客戶端自行拼 _has 查詢
POST /MedicationRequest/{id}/$sign POST 簽署醫囑:驗證病程來源存在 → 寫入 progressSnapshot → 建立 Provenance.signature
POST /MedicationRequest/{id}/$submit POST 送 HIS:產生 outbound_request、設 hisTransmissionStatus=sent。冪等,見 06
POST /Composition/{id}/$sign POST 文件簽署,同上簽章流程
POST /Composition/{id}/$receive POST 交班接收:建立 Provenance + 轉移 Task.owner

$roster 回應

{
  "resourceType": "Bundle", "type": "searchset",
  "entry": [{
    "resource": { "resourceType": "Patient", "id": "abc" },
    "search": { "mode": "match" }
  }, {
    "resource": { "resourceType": "CareTeam", "id": "ct-1" },
    "search": { "mode": "include" }
  }]
}

作用中醫囑筆數不放進 Patient(那不是 Patient 的屬性),改由客戶端對 MedicationRequest?patient=…&status=active&_summary=count 取得,或由 $roster 在 Bundle.entry.extension[activeOrderCount] 帶出。

3.9 快取