05 · 錯誤、安全與稽核

5.1 OperationOutcome

所有非 2xx 回應的 body 一律是 OperationOutcome,且每個 issue 必須齊備四項:

元素 必填 內容
severity fatal / error / warning / information
code R4 標準 issue-typebusiness-ruleconflictrequiredvalueforbiddeninvariant…)
details.coding 本院錯誤碼,system = http://icu.emr.local/CodeSystem/api-error
diagnostics 給臨床使用者看的繁中訊息,可直接顯示在畫面
expression 條件 欄位層級錯誤時必填,FHIRPath 路徑

diagnostics 是使用者可見文字,不是給工程師的堆疊訊息。原型的訊息文字(例如「需逐項人工確認五對(模擬),不能由單次掃碼推定」)可直接沿用,去掉「(模擬)」字樣。

{
  "resourceType": "OperationOutcome",
  "issue": [{
    "severity": "error",
    "code": "business-rule",
    "details": {
      "coding": [{
        "system": "http://icu.emr.local/CodeSystem/api-error",
        "code": "FIVE-RIGHTS-INCOMPLETE"
      }]
    },
    "diagnostics": "需逐項人工確認五對,不能由單次掃碼推定。",
    "expression": ["MedicationAdministration.extension('…/verificationChecklist')"]
  }]
}

5.2 錯誤碼表

錯誤碼 HTTP issue.code 規則
UNKNOWN-ACTOR 401 login BR-AUTH-001
ROLE-DOCTOR-ONLY 403 forbidden BR-AUTH-002
ROLE-NURSE-ONLY 403 forbidden BR-AUTH-004
CONSULT-READ-ONLY 403 forbidden BR-AUTH-003
ACTOR-NOT-ASSIGNABLE 422 business-rule BR-AUTH-006
PATIENT-REQUIRED 422 required BR-CTX-001
PATIENT-OUT-OF-SCOPE 403 forbidden BR-CTX-002
ENCOUNTER-MISMATCH 422 invariant BR-CTX-003
ROSTER-NOT-CONFIRMED 409 business-rule BR-CTX-004
SOURCE-PATIENT-MISMATCH 422 invariant BR-CTX-005
READ-ONLY-SOURCE 422 business-rule §1.2.1, BR-RPT-001
PROGRESS-NOTE-REQUIRED 422 required BR-ORD-001
SIGN-OWN-DRAFT-ONLY 403 forbidden BR-ORD-002, BR-DOC-004
SUBMIT-STATE-INVALID 409 conflict BR-ORD-004
HIS-RESPONSE-INVALID 422 value BR-ORD-005
ORDER-IMMUTABLE-AFTER-SUBMIT 409 conflict BR-ORD-006
RESEND-BLOCKED-UNKNOWN 409 business-rule BR-ORD-007
CODE-REQUIRED 422 required BR-MED-001
ORDER-NOT-EXECUTABLE 409 business-rule BR-MED-002
FIVE-RIGHTS-INCOMPLETE 422 business-rule BR-MED-003
VARIANCE-REASON-REQUIRED 422 required BR-MED-004
DOSE-ALREADY-ADMINISTERED 200 duplicate BR-MED-005
TIME-OUT-OF-ORDER-PERIOD 422 invariant BR-MED-006
ACTUAL-VALUE-REQUIRED 422 required BR-MED-007
REVIEW-NOTE-REQUIRED 422 required BR-MED-008, BR-RPT-003
VALUE-INVALID 422 value BR-OBS-001
BP-COMPONENTS-REQUIRED 422 required BR-OBS-002
ZERO-AS-MISSING 422 business-rule BR-OBS-003
INFUSION-SOURCE-REQUIRED 422 required BR-OBS-004
REPORT-NOT-AVAILABLE 409 business-rule BR-RPT-002
SECTION-REQUIRED 422 required BR-DOC-001~003
SIGNED-IMMUTABLE 409 conflict BR-DOC-005
AMEND-REASON-REQUIRED 422 required BR-DOC-005, BR-HO-002
NO-COPYABLE-PROGRESS 409 business-rule BR-DOC-006
CATALOG-INACTIVE 422 business-rule BR-CAT-001, BR-DOC-009
CATALOG-CHANGED 409 conflict BR-CAT-003
TASK-NOT-OWNER 403 forbidden BR-TASK-001
EVIDENCE-REQUIRED 422 required BR-TASK-002
SOURCE-NOT-PROCESSED 409 business-rule BR-TASK-002
SUMMARY-REQUIRED 422 required BR-HO-001
HANDOVER-SUPERSEDED 409 conflict BR-HO-002, BR-HO-005
HANDOVER-ROLE-MISMATCH 403 forbidden BR-HO-003
HANDOVER-ALREADY-RECEIVED 409 duplicate BR-HO-006
ASSIGNEE-INVALID 422 value BR-CONS-001
CONSULT-NOT-ASSIGNEE 403 forbidden BR-CONS-002
CONSULT-ALREADY-ANSWERED 409 conflict BR-CONS-003
RECOMMENDATION-REQUIRED 422 required BR-CONS-004
TIMEZONE-REQUIRED 422 value BR-TIME-001
TIME-INVALID 422 value BR-TIME-002
HIS-UNAVAILABLE 502 transient 06
HIS-TIMEOUT 504 timeout 06

5.3 HTTP 狀態碼使用

用於
200 讀取成功、update 成功、條件式建立命中既有資源
201 create 成功。必帶 LocationETag
304 If-None-Match 命中
400 語法錯誤(JSON 壞掉、參數不合法)
401 未認證或權杖失效
403 已認證但無權(角色、病人範圍)
404 資源不存在。不因無權而回 404,權限問題一律 403
409 版本衝突、狀態不允許
412 If-Match
422 資源結構合法但違反業務規則
429 節流
502 / 504 HIS 不可用/逾時

5.4 保存失敗的語義(與原型的重要差異)

原型的 store 在持久化失敗時仍把交易寫進記憶體,只顯示警告——那是 localStorage 原型的權宜做法。

正式 API 不得如此。 未持久化就不能回 2xx:

情況 原型 正式 API
寫入儲存失敗 保留在記憶體 + 警示 500,不回 201,資源不存在
並行版本衝突 保留畫面內容 + 警示 409 + 目前版本,客戶端合併後重送
HIS 逾時 unknown 狀態 504,本地醫囑仍為 senthisTransmissionStatus=unknown(本地已持久化)

客戶端保留使用者輸入是 UI 的責任,不是靠伺服器回假成功。

scenario(保存失敗/來源缺漏/HIS 逾時)是原型的情境切換開關,正式 API 沒有這個端點。

5.5 認證與授權

5.5.1 SMART on FHIR

採 OAuth 2.0 + SMART on FHIR user/ scopes(後端服務用 system/)。

存取權杖必須帶:

Claim 內容
sub 使用者識別
fhirUser PractitionerRole/{id} — 決定角色與科別
shift 班別代碼(決定「本班」時間窗與交班對象)
scope 見下表

5.5.2 Scope 對照

角色 Scope
主責醫師 user/Patient.rs user/Encounter.rs user/MedicationRequest.cru user/ServiceRequest.cru user/Composition.cru user/Condition.cru user/Goal.cru user/CarePlan.cs user/Task.cru user/Provenance.cs user/Communication.cs user/DiagnosticReport.rs user/Observation.rs
護理師 user/Patient.rs user/Encounter.rs user/MedicationRequest.rs user/ServiceRequest.rs user/MedicationAdministration.cs user/Procedure.cs user/Observation.crs user/Composition.cru user/Task.cru user/Communication.cs user/DiagnosticReport.rs
照會醫師 user/Patient.rs user/Encounter.rs user/DiagnosticReport.rs user/Observation.rs user/Composition.rs user/ServiceRequest.rs + 僅限已指派照會的病人
整合服務 system/MedicationRequest.u system/ServiceRequest.u system/DiagnosticReport.cu system/Observation.cu system/Specimen.cu system/Patient.cu system/Encounter.cu

c=create、r=read、u=update、d=delete、s=search)

Scope 是必要條件,不是充分條件。 通過 scope 後仍要跑 04 的全部規則——例如護理師有 MedicationAdministration.c,但 BR-MED-002BR-MED-003 仍可能擋下。

5.5.3 病人範圍

除了 scope,伺服器對每個請求做 compartment 檢查:

  1. fhirUser 取得作用中的 CareTeam 參與關係。
  2. 照會醫師另外檢查是否有指派給他、且 status=active 的照會 ServiceRequest
  3. 目標資源的 subjectpatient 不在集合內 → 403 PATIENT-OUT-OF-SCOPE

搜尋一律隱含加上病人範圍過濾,不是回 403 而是回較少的結果——避免透過錯誤碼探測病人是否存在。單一資源 read 才回 403。

5.6 稽核

AuditEvent vs Provenance

兩者都要,用途不同:

AuditEvent Provenance
目的 誰在何時存取/異動了什麼(安全稽核) 這筆資料怎麼來的(臨床溯源)
產生者 伺服器自動,每個請求一筆 業務動作明確產生
臨床端可見 否(僅稽核人員) 是(畫面顯示作者、時間、引用來源)
對應原型 state.audit[] 病歷的引用來源、已閱/判讀、簽章

AuditEvent 必填

{
  "resourceType": "AuditEvent",
  "type": { "system": "http://terminology.hl7.org/CodeSystem/audit-event-type", "code": "rest" },
  "subtype": [{ "system": "http://hl7.org/fhir/restful-interaction", "code": "create" }],
  "action": "C",
  "recorded": "2026-08-31T09:12:04+07:00",
  "outcome": "0",
  "agent": [{
    "who": { "reference": "PractitionerRole/d1" },
    "requestor": true,
    "network": { "address": "10.2.3.4", "type": "2" }
  }],
  "source": { "observer": { "reference": "Device/emr-api" } },
  "entity": [{
    "what": { "reference": "MedicationAdministration/9a2" },
    "type": { "code": "2" }
  }]
}

異動理由

audit_event.change_reason(S06 標為 M)→ 寫入時以 X-Change-Reason 標頭帶入,伺服器落到 AuditEvent.extension[changeReason]。診斷管理等需要理由的操作缺此標頭時回 422

5.6.1 電子簽章驗證

Provenance.signature.data 的驗證結果(signature_hash 與驗證服務輸出,取得=C)由伺服器產生,透過 Provenanceextension[signatureValidation] 回傳:valid / invalid / unverified(憑證服務不可用時用 unverified不預設 valid)。

5.7 傳輸與記錄