03 · CRUD 與搜尋規格

3.1 基本約定

項目
Base URL https://{host}/fhir/r4
Content-Type / Accept application/fhir+json
能力宣告 GET /metadatacapabilitystatement.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 建立後不可改的資源

MedicationAdministrationProcedureObservationCommunication 一旦建立就是已發生事實的紀錄,只能標 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 OKETag: W/"5"
版本落後 409 ConflictOperationOutcome(code=conflict)
If-Match 412 Precondition Failed

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

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

{
  "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.linkself / 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-idrelatesTo), 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 × MedicationRequestServiceRequest,共用同一 extension[orderSet]
簽署文件 Composition 更新 + Provenance 建立
接收交班 Provenance 建立 + N × Task.owner 更新
病歷更正 Composition 建立 + 舊版 status 更新

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

batch(非原子)

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

3.8 自訂操作

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

操作 方法 說明
GET /Patient/$roster GET 回傳目前登入者的照護名單(Bundle,含 PatientCareTeam+作用中醫囑計數)。取代客戶端自行拼 _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 取得,或由 $rosterBundle.entry.extension[activeOrderCount] 帶出。

3.9 快取