openapi: 3.0.3

info:
  title: ICU EMR FHIR API（越南兒童醫院 Phase 1）
  version: 0.1.0
  description: |
    ICU 臨床工作站的 FHIR R4 (4.0.1) 介面。本文件用於與院方及 HIS 廠商溝通介面範圍與約束。

    ### 三個必須先講清楚的前提

    1. **這是 EMR 的介面，不是 HIS 的介面。** HIS 仍是病人、就醫、檢驗結果、處方號的權威來源。
       EMR 透過整合層與 HIS 往來（`PrescriptionCreated` / `ServiceRequest*` / `MedicationChangeStatus`），
       臨床端不直接觸碰那些契約。
    2. **醫囑有三個獨立狀態軸**：臨床狀態（`status`）、本地簽署（`Provenance.signature`）、
       HIS 傳輸（`extension[hisTransmissionStatus]`）。不可合併成單一 `status`。
       HIS 的 `MedicationChangeStatus` 1–5（新開立／審核／匯總領取／發放／急救車補充）**都不代表已給藥**。
    3. **缺值不補。** 沒有來源就用 `dataAbsentReason` 或省略元素，不送 0、不預設正常、不推定未執行。

    ### 資料權威（決定哪些欄位可寫）

    29 份 S／N 規格的 1,209 個欄位都標了「取得方式」，直接對映到 API 可寫性：

    | 取得 | 意義 | 欄位數 | API |
    | --- | --- | --- | --- |
    | M | 手動輸入 | 431 | 可寫 |
    | H | HIS 來源 | 425 | 唯讀，僅整合服務帳號可寫 |
    | P | 引用其他資源 | 264 | 唯讀，以 Reference 表達 |
    | C | 系統計算 | 88 | 唯讀，伺服器產生 |
    | D | 設備來源 | 1 | 唯讀 |

    ### 通則

    - 所有可寫互動要求 `If-Match`（樂觀鎖）。缺少時回 `412`。
    - **不提供 `DELETE`。** 誤建資料以 `status = entered-in-error` 標記。
    - 業務規則編號 `BR-*` 對應規格文件《04 業務規則與狀態機》，可直接作為測試案例編號。
    - 錯誤碼見 `OperationOutcome` schema 的 `ApiErrorCode` 列舉。

    ### Schema 範圍說明

    本文件的資源 schema **只列出本專案實際約束的元素**（必填、唯讀、本地擴充、值域限制），
    其餘元素依 FHIR R4 base 定義，未在此重複。因此所有資源 schema 皆為 `additionalProperties: true`。
  contact:
    name: ICU Phase 1 專案
  license:
    name: 院內文件，未對外授權

externalDocs:
  description: 完整規格（設計原則、資源對照、業務規則、HIS 整合）
  url: ../README.md

servers:
  - url: https://{host}/fhir/r4
    description: EMR FHIR Server
    variables:
      host:
        default: emr.icu.local

security:
  - smartOnFhir: []

tags:
  - name: 系統
    description: 能力宣告與 transaction
  - name: 病人與就醫
    description: HIS 唯讀資源，以及本班照護名單
  - name: 醫囑
    description: 藥物醫囑與醫令（檢驗／檢查／照護）
  - name: 實際執行
    description: 給藥執行、處置執行、醫師核對
  - name: 檢驗與量測
    description: 報告、檢驗指標、生命徵象
  - name: 病歷文件
    description: Admission／Progress／Discharge Note、會診回覆、SBAR 交班、同意書
  - name: 工作與交班
    description: 臨床工作待辦與交班承接
  - name: 溯源與稽核
    description: Provenance（臨床溯源）與 AuditEvent（安全稽核）
  - name: 目錄
    description: B 目錄品項查詢

paths:
  # ────────────────────────────── 系統 ──────────────────────────────
  /metadata:
    get:
      tags: [系統]
      summary: 能力宣告
      operationId: getCapabilityStatement
      description: 回傳 CapabilityStatement，列出本伺服器支援的資源、互動與搜尋參數。
      security: []
      responses:
        '200':
          description: 能力宣告
          content:
            application/fhir+json:
              schema:
                type: object
                additionalProperties: true

  /:
    post:
      tags: [系統]
      summary: 提交 Bundle（transaction / batch）
      operationId: submitBundle
      description: |
        **transaction**（原子，全成功或全失敗）用於以下情境：

        | 情境 | 內容 |
        | --- | --- |
        | 一次開立多項醫囑 | N × MedicationRequest／ServiceRequest，共用 `orderSet` |
        | 簽署文件 | Composition 更新 ＋ Provenance 建立 |
        | 接收交班 | Provenance 建立 ＋ N × Task.owner 更新 |
        | 病歷更正 | 新 Composition 建立 ＋ 舊版 status 更新 |
        | HIS 檢驗結果落地 | Specimen ＋ N × Observation ＋ DiagnosticReport |

        **batch** 僅限唯讀批次載入，含寫入時回 `422`。

        一次批次建立的醫囑只保證「建立」原子；後續簽署、送出、執行、回報各自獨立（BR-ORD-009）。
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema:
              $ref: '#/components/schemas/Bundle'
      responses:
        '200':
          description: 每個 entry 的個別結果（`Bundle.type = transaction-response`）
          content:
            application/fhir+json:
              schema:
                $ref: '#/components/schemas/Bundle'
        '400': { $ref: '#/components/responses/BadRequest' }
        '401': { $ref: '#/components/responses/Unauthorized' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  # ────────────────────────── 病人與就醫 ──────────────────────────
  /Patient:
    get:
      tags: [病人與就醫]
      summary: 搜尋病人（HIS 唯讀）
      operationId: searchPatient
      description: |
        結果一律隱含加上病人範圍過濾（只回操作者照護中的病人）。
        **不會因為無權而回 403，而是回較少的結果**——避免透過錯誤碼探測病人是否存在。
      parameters:
        - { name: identifier, in: query, schema: { type: string }, description: 病歷號或身分識別碼 }
        - { name: name, in: query, schema: { type: string } }
        - { name: birthdate, in: query, schema: { type: string }, description: 'FHIR date 前綴語法，例：ge2020-01-01' }
        - $ref: '#/components/parameters/count'
        - $ref: '#/components/parameters/sort'
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }
        '401': { $ref: '#/components/responses/Unauthorized' }

  /Patient/{id}:
    parameters:
      - $ref: '#/components/parameters/id'
    get:
      tags: [病人與就醫]
      summary: 讀取病人
      operationId: readPatient
      responses:
        '200':
          description: 病人
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/Patient' }
        '304': { description: 未變更（`If-None-Match` 命中） }
        '403': { $ref: '#/components/responses/Forbidden' }
        '404': { $ref: '#/components/responses/NotFound' }

  /Patient/$roster:
    get:
      tags: [病人與就醫]
      summary: 本班照護名單
      operationId: getRoster
      description: |
        回傳目前登入者的照護名單（Patient ＋ CareTeam ＋ 作用中醫囑計數）。

        - 主責醫師／護理師：取其 `CareTeam` 參與關係。
        - 照會醫師：只回**已指派給他且尚未完成**的照會病人。
        - 作用中醫囑計數只計 `status=active` 且 `hisTransmissionStatus=accepted` 者。
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }
        '401': { $ref: '#/components/responses/Unauthorized' }

  /Encounter:
    get:
      tags: [病人與就醫]
      summary: 搜尋就醫事件（HIS 唯讀）
      operationId: searchEncounter
      parameters:
        - $ref: '#/components/parameters/patient'
        - { name: status, in: query, schema: { type: string } }
        - { name: date, in: query, schema: { type: string } }
        - { name: identifier, in: query, schema: { type: string } }
        - $ref: '#/components/parameters/count'
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }

  /CareTeam:
    get:
      tags: [病人與就醫]
      summary: 搜尋照護團隊
      operationId: searchCareTeam
      parameters:
        - $ref: '#/components/parameters/patient'
        - { name: participant, in: query, schema: { type: string } }
        - { name: status, in: query, schema: { type: string } }
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }
    post:
      tags: [病人與就醫]
      summary: 建立照護團隊
      operationId: createCareTeam
      description: |
        「確認照護名單」這個動作**不是** CareTeam 上的一個時間欄位，而是另建一筆
        `Provenance`（`activity = roster-confirm`，`target = CareTeam/…`）。
        理由：同一份名單可被不同班別、不同人各自確認一次，每次都要留人與時間。
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/CareTeam' }
      responses:
        '201': { $ref: '#/components/responses/Created' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  # ──────────────────────────── 醫囑 ────────────────────────────
  /MedicationRequest:
    get:
      tags: [醫囑]
      summary: 搜尋藥物醫囑
      operationId: searchMedicationRequest
      description: |
        建議搭配 `_revinclude=MedicationAdministration:request` 一次帶回執行紀錄，
        這正好是 eMAR 工作表的資料需求。
      parameters:
        - $ref: '#/components/parameters/patient'
        - $ref: '#/components/parameters/encounter'
        - { name: status, in: query, schema: { type: string, enum: [draft, active, on-hold, cancelled, completed, stopped, entered-in-error, unknown] } }
        - { name: intent, in: query, schema: { type: string } }
        - { name: authoredon, in: query, schema: { type: string } }
        - { name: requester, in: query, schema: { type: string } }
        - { name: identifier, in: query, schema: { type: string }, description: 'HIS 處方號或每行藥品碼' }
        - { name: code, in: query, schema: { type: string } }
        - $ref: '#/components/parameters/revinclude'
        - $ref: '#/components/parameters/include'
        - $ref: '#/components/parameters/count'
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }
    post:
      tags: [醫囑]
      summary: 開立藥物醫囑（草稿）
      operationId: createMedicationRequest
      description: |
        建立後為 `status=draft`、未簽署、`hisTransmissionStatus=notSent`。

        **必須通過的規則**

        | 規則 | 內容 |
        | --- | --- |
        | BR-AUTH-002 | 限主責醫師（照會醫師不可開立） |
        | BR-CTX-002 | 病人必須在操作者照護範圍內 |
        | BR-CTX-004 | 必須已確認本班照護名單 |
        | BR-ORD-001 | 必須指定病程來源（同病人、同作者、已存在的 Composition） |
        | BR-MED-001 | 藥品碼與給藥途徑必須帶 `coding.system` ＋ `code`，只給 `text` 拒絕 |
        | BR-CAT-001 | 停用品項不可用於新開立 |

        `requester` 一律取自存取權杖，客戶端送的值被忽略（BR-AUTH-006）。
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/MedicationRequest' }
      responses:
        '201': { $ref: '#/components/responses/Created' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  /MedicationRequest/{id}:
    parameters:
      - $ref: '#/components/parameters/id'
    get:
      tags: [醫囑]
      summary: 讀取藥物醫囑
      operationId: readMedicationRequest
      responses:
        '200':
          description: 藥物醫囑
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/MedicationRequest' }
              examples:
                active:
                  $ref: '#/components/examples/MedicationRequestActive'
        '404': { $ref: '#/components/responses/NotFound' }
    put:
      tags: [醫囑]
      summary: 更新藥物醫囑
      operationId: updateMedicationRequest
      description: |
        **可寫範圍取決於狀態（BR-ORD-006）**

        | 狀態 | 可改 | 不可改 |
        | --- | --- | --- |
        | `draft` 未簽 | 全部欄位 | — |
        | 已簽、未送出 | `status`（→ `cancelled`） | 藥品、劑量、途徑、頻次 |
        | 已送出 HIS | 無 | 全部 |
        | `active`（HIS 已接受） | `status`（→ `stopped`） | 其餘 |

        已送出的醫囑要改內容**必須取消再重開**——HIS 端 `hisPrescriptionNumber` 已產生，
        就地改會讓兩端不一致。
      parameters:
        - $ref: '#/components/parameters/ifMatch'
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/MedicationRequest' }
      responses:
        '200': { $ref: '#/components/responses/Updated' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }
    patch:
      tags: [醫囑]
      summary: 部分更新（JSON Patch）
      operationId: patchMedicationRequest
      description: 主要用途是標記 `entered-in-error`，或在允許的狀態下改 `status`。
      parameters:
        - $ref: '#/components/parameters/ifMatch'
      requestBody:
        required: true
        content:
          application/json-patch+json:
            schema: { $ref: '#/components/schemas/JsonPatch' }
            example:
              - { op: replace, path: /status, value: entered-in-error }
      responses:
        '200': { $ref: '#/components/responses/Updated' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  /MedicationRequest/{id}/$sign:
    parameters:
      - $ref: '#/components/parameters/id'
    post:
      tags: [醫囑]
      summary: 簽署醫囑（模擬電子簽章）
      operationId: signMedicationRequest
      description: |
        原子完成三件事：

        1. 驗證病程來源存在且屬於同病人、同作者（BR-ORD-001）。
        2. 把病程來源的**當下版本**快照寫入 `supportingInformation`（帶版本 Reference，BR-ORD-003）。
        3. 建立 `Provenance.signature`。

        只能簽自己建立且尚未簽署的醫囑（BR-ORD-002）。
        簽署前重新驗證品項仍有效；已停用則回 `409 CATALOG-CHANGED`（BR-CAT-003）。
      parameters:
        - $ref: '#/components/parameters/ifMatch'
      responses:
        '200':
          description: 已簽署，回傳更新後的醫囑與新建的 Provenance
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/Bundle' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  /MedicationRequest/{id}/$submit:
    parameters:
      - $ref: '#/components/parameters/id'
    post:
      tags: [醫囑]
      summary: 送出至 HIS
      operationId: submitMedicationRequest
      description: |
        產生 `PrescriptionCreated` 事件並設 `hisTransmissionStatus=sent`。**冪等**：
        同一資源同一版本重送會回傳首次結果，不重打 HIS。

        **僅接受「已簽署且 `notSent`」**（BR-ORD-004）。

        ### 送出前阻擋條件（回 422，醫囑維持已簽未送）

        | 編號 | 條件 | 錯誤碼 |
        | --- | --- | --- |
        | BR-INT-001 | 病程來源為空 | `PROGRESS-NOTE-REQUIRED` |
        | BR-INT-002 | 缺 `dispenseStockCode` 對照 | `DISPENSE-STOCK-UNMAPPED` |
        | BR-INT-003 | `medicationConsultationRequest` 為 null（三態未解析） | `TRISTATE-NOT-RESOLVED` |
        | BR-INT-005 | 四時段單位不一致 | `DOSE-UNIT-INCONSISTENT` |
        | BR-INT-006 | `archiveId` 非本次就醫的當前值 | `ARCHIVE-ID-STALE` |

        ### `hisTransmissionStatus = unknown` 時禁止重送

        回 `409 RESEND-BLOCKED-UNKNOWN`。必須由人確認 HIS 端的實際結果，
        自動重送可能造成重複處方（BR-ORD-007）。
      parameters:
        - $ref: '#/components/parameters/ifMatch'
      responses:
        '200':
          description: 已送出並取得 HIS 回應
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/MedicationRequest' }
        '409':
          description: 狀態不允許送出，或 `unknown` 狀態禁止重送
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/OperationOutcome' }
              example:
                resourceType: OperationOutcome
                issue:
                  - severity: error
                    code: business-rule
                    details:
                      coding:
                        - system: http://icu.emr.local/CodeSystem/api-error
                          code: RESEND-BLOCKED-UNKNOWN
                    diagnostics: 介接回應未知，結果無法確認。請先查明原送出請求，不可重送。
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }
        '502': { $ref: '#/components/responses/HisUnavailable' }
        '504': { $ref: '#/components/responses/HisTimeout' }

  /ServiceRequest:
    get:
      tags: [醫囑]
      summary: 搜尋醫令（檢驗／檢查／照護／跨科照會）
      operationId: searchServiceRequest
      description: '跨科照會也是 ServiceRequest，以 `category=consultation` 區分。'
      parameters:
        - $ref: '#/components/parameters/patient'
        - $ref: '#/components/parameters/encounter'
        - { name: status, in: query, schema: { type: string } }
        - { name: category, in: query, schema: { type: string } }
        - { name: code, in: query, schema: { type: string } }
        - { name: priority, in: query, schema: { type: string, enum: [routine, urgent, asap, stat] } }
        - { name: performer, in: query, schema: { type: string } }
        - { name: identifier, in: query, schema: { type: string }, description: 'HIS 需求單號或詳細行碼' }
        - $ref: '#/components/parameters/count'
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }
    post:
      tags: [醫囑]
      summary: 開立醫令
      operationId: createServiceRequest
      description: |
        規則同藥物醫囑（BR-AUTH-002、BR-CTX-002/004、BR-ORD-001、BR-CAT-001）。

        **`priority` 值域衝突（未決事項 Q-05）**：HIS 建立用 `severity` 1 正常／2 急／3 特急，
        修改介面則無值域，兩者不能直接互換。EMR 內部一律用 FHIR `priority`，
        由整合層對映；**修改時不送 priority**。
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/ServiceRequest' }
      responses:
        '201': { $ref: '#/components/responses/Created' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  /ServiceRequest/{id}:
    parameters:
      - $ref: '#/components/parameters/id'
    get:
      tags: [醫囑]
      summary: 讀取醫令
      operationId: readServiceRequest
      responses:
        '200':
          description: 醫令
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/ServiceRequest' }
        '404': { $ref: '#/components/responses/NotFound' }
    put:
      tags: [醫囑]
      summary: 更新醫令
      operationId: updateServiceRequest
      description: |
        送 HIS 的 `ServiceRequestUpdated` 有兩種語義，取決於是否帶舊詳細行碼：

        | 帶不帶 `serviceRequestNumber` | HIS 行為 | FHIR 表達 |
        | --- | --- | --- |
        | 帶舊詳細行碼 | HIS 取消舊服務並新增新服務 | 舊筆 `status=revoked`，新建一筆 `replaces` 舊的 |
        | 不帶 | 純新增 | 新建 ServiceRequest |
      parameters:
        - $ref: '#/components/parameters/ifMatch'
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/ServiceRequest' }
      responses:
        '200': { $ref: '#/components/responses/Updated' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  # ────────────────────────── 實際執行 ──────────────────────────
  /MedicationAdministration:
    get:
      tags: [實際執行]
      summary: 搜尋給藥執行紀錄
      operationId: searchMedicationAdministration
      parameters:
        - $ref: '#/components/parameters/patient'
        - { name: request, in: query, schema: { type: string }, description: 來源醫囑 }
        - { name: status, in: query, schema: { type: string, enum: [in-progress, not-done, on-hold, completed, entered-in-error, stopped, unknown] } }
        - { name: effective-time, in: query, schema: { type: string } }
        - { name: performer, in: query, schema: { type: string } }
        - { name: identifier, in: query, schema: { type: string }, description: 劑次識別碼 }
        - $ref: '#/components/parameters/count'
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }
    post:
      tags: [實際執行]
      summary: 記錄給藥執行
      operationId: createMedicationAdministration
      description: |
        **建立後不可修改。** 誤建以 `entered-in-error` 標記後重建。

        ### 必須通過的規則

        | 規則 | 內容 |
        | --- | --- |
        | BR-AUTH-004 | 限護理師 |
        | BR-MED-002 | 來源醫囑必須 `status=active` **且** `hisTransmissionStatus=accepted` |
        | BR-MED-003 | `completed` 時五對核對必須**全部** `true`，不可由單次掃碼推定 |
        | BR-MED-004 | 實際值與醫囑不同、或狀態非 `completed` 時，原因必填 |
        | BR-MED-005 | 同一劑次至多一筆 `completed` |
        | BR-MED-006 | 實際時間必須落在該醫囑版本有效期間 `[start, end)` |
        | BR-MED-007 | `completed` 時實際劑量與途徑必填，不得以醫囑預定值當事實 |

        ### 劑次唯一性用條件式建立

        帶 `If-None-Exist`；已存在同劑次完成紀錄時回 **`200`**（回傳既有資源）而非 `201`。
        比「先查再寫」安全：不會有兩個護理師同時通過檢查的競態。
      parameters:
        - $ref: '#/components/parameters/ifNoneExist'
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/MedicationAdministration' }
            examples:
              completed:
                $ref: '#/components/examples/MedicationAdministrationCompleted'
      responses:
        '200':
          description: 本劑次已有完成紀錄，回傳既有資源（`DOSE-ALREADY-ADMINISTERED`）
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/MedicationAdministration' }
        '201': { $ref: '#/components/responses/Created' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409':
          description: 來源醫囑不在可執行狀態
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/OperationOutcome' }
        '422':
          description: 違反業務規則（五對未完成、缺實際值、時間超出有效期間…）
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/OperationOutcome' }
              examples:
                fiveRights:
                  $ref: '#/components/examples/FiveRightsIncomplete'

  /Procedure:
    post:
      tags: [實際執行]
      summary: 記錄照護／處置執行
      operationId: createProcedure
      description: |
        照護醫令以 ServiceRequest 開立、以 Procedure 記錄執行。建立後不可修改。

        手術紀錄在 Phase 1 為**唯讀接收**，EMR 不提供編修或代簽。
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/Procedure' }
      responses:
        '201': { $ref: '#/components/responses/Created' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }
    get:
      tags: [實際執行]
      summary: 搜尋處置執行
      operationId: searchProcedure
      parameters:
        - $ref: '#/components/parameters/patient'
        - { name: based-on, in: query, schema: { type: string } }
        - { name: status, in: query, schema: { type: string } }
        - { name: date, in: query, schema: { type: string } }
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }

  # ────────────────────────── 檢驗與量測 ──────────────────────────
  /DiagnosticReport:
    get:
      tags: [檢驗與量測]
      summary: 搜尋檢驗／檢查報告（HIS 唯讀）
      operationId: searchDiagnosticReport
      description: |
        一次載入 S13 檢驗結果頁所需的完整資料：

        ```
        GET /DiagnosticReport?patient=Patient/abc&issued=ge2026-08-30
            &_include=DiagnosticReport:result
            &_include=DiagnosticReport:specimen
            &_revinclude=Provenance:target
        ```

        帶回報告、逐項數值、檢體，以及已閱／判讀的 Provenance。

        **無 `interpretation` 的結果不得歸類為正常**（BR-RPT-005）。
        要查正常值必須明寫 `interpretation=N`。
      parameters:
        - $ref: '#/components/parameters/patient'
        - { name: status, in: query, schema: { type: string, enum: [registered, partial, preliminary, final, amended, corrected, appended, cancelled, entered-in-error, unknown] } }
        - { name: category, in: query, schema: { type: string } }
        - { name: code, in: query, schema: { type: string } }
        - { name: issued, in: query, schema: { type: string } }
        - { name: based-on, in: query, schema: { type: string } }
        - $ref: '#/components/parameters/include'
        - $ref: '#/components/parameters/revinclude'
        - $ref: '#/components/parameters/count'
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }

  /DiagnosticReport/{id}:
    parameters:
      - $ref: '#/components/parameters/id'
    get:
      tags: [檢驗與量測]
      summary: 讀取報告
      operationId: readDiagnosticReport
      description: |
        **EMR 不可寫入報告。** 臨床端的「已閱」「判讀」一律另建 `Provenance`，
        不去改報告本身（BR-RPT-001）。
      responses:
        '200':
          description: 報告
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/DiagnosticReport' }
        '404': { $ref: '#/components/responses/NotFound' }
    put:
      tags: [檢驗與量測]
      summary: 更新報告（僅整合服務帳號）
      operationId: updateDiagnosticReport
      description: |
        臨床使用者帳號呼叫一律回 `422 READ-ONLY-SOURCE`。
        僅具 `system/DiagnosticReport.u` scope 的整合服務可寫。
      parameters:
        - $ref: '#/components/parameters/ifMatch'
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/DiagnosticReport' }
      responses:
        '200': { $ref: '#/components/responses/Updated' }
        '422':
          description: 臨床帳號嘗試寫入 HIS 來源
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/OperationOutcome' }
              example:
                resourceType: OperationOutcome
                issue:
                  - severity: error
                    code: business-rule
                    details:
                      coding:
                        - system: http://icu.emr.local/CodeSystem/api-error
                          code: READ-ONLY-SOURCE
                    diagnostics: DiagnosticReport 由 HIS 提供，EMR 不接受寫入。已閱與判讀請建立 Provenance。
                    expression: [DiagnosticReport.issued]

  /Observation:
    get:
      tags: [檢驗與量測]
      summary: 搜尋量測與檢驗指標
      operationId: searchObservation
      description: |
        **時間窗查詢**（對應工作站的三種時間窗）：

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

        伺服器**不提供** `window=shift` 這種語意參數——班別起訖屬排班資料，
        換算責任在客戶端，避免伺服器對「本班」有隱含定義。
      parameters:
        - $ref: '#/components/parameters/patient'
        - { name: code, in: query, schema: { type: string } }
        - { name: component-code, in: query, schema: { type: string }, description: '血壓子項用，例：收縮壓 8480-6' }
        - { name: date, in: query, schema: { type: string } }
        - { name: category, in: query, schema: { type: string, enum: [vital-signs, laboratory, procedure] } }
        - { name: part-of, in: query, schema: { type: string } }
        - $ref: '#/components/parameters/sort'
        - $ref: '#/components/parameters/count'
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }
    post:
      tags: [檢驗與量測]
      summary: 記錄生理量測
      operationId: createObservation
      description: |
        建立後不可修改。

        | 規則 | 內容 |
        | --- | --- |
        | BR-AUTH-004 | 限護理師 |
        | BR-OBS-001 | 項目須在支援清單內，數值須為非負有限數 |
        | BR-OBS-002 | **血壓必須以單一 Observation 的兩個 component 表達，且兩子項同時提供** |
        | BR-OBS-003 | 缺值一律 `dataAbsentReason`，**禁止送 0 代替** |
        | BR-OBS-004 | 輸注速率觀察須 `partOf` 一筆執行紀錄，且 `notCountedAsIntake=true` |

        血壓拆成兩筆獨立 Observation 會讓收縮／舒張壓在時間軸上失去配對，因此明文禁止。
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/Observation' }
            examples:
              bloodPressure:
                $ref: '#/components/examples/ObservationBloodPressure'
      responses:
        '201': { $ref: '#/components/responses/Created' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  # ────────────────────────── 病歷文件 ──────────────────────────
  /Composition:
    get:
      tags: [病歷文件]
      summary: 搜尋病歷文件
      operationId: searchComposition
      description: |
        | 文件 | `type` |
        | --- | --- |
        | Admission Note | LOINC `34117-2` |
        | Progress Note（SOAP） | LOINC `11506-3` |
        | Discharge Summary | LOINC `18842-5` |
        | 會診／照會回覆 | LOINC `11488-4` |
        | 護理紀錄 | 本地碼【待確認】 |
        | SBAR 交班 | 本地碼【未決事項 Q-04】 |
      parameters:
        - $ref: '#/components/parameters/patient'
        - $ref: '#/components/parameters/encounter'
        - { name: type, in: query, schema: { type: string } }
        - { name: status, in: query, schema: { type: string, enum: [preliminary, final, amended, entered-in-error] } }
        - { name: date, in: query, schema: { type: string } }
        - { name: author, in: query, schema: { type: string } }
        - { name: related-id, in: query, schema: { type: string }, description: 依 relatesTo 目標查更正鏈 }
        - $ref: '#/components/parameters/count'
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }
    post:
      tags: [病歷文件]
      summary: 建立病歷文件（草稿）
      operationId: createComposition
      description: |
        建立為 `status=preliminary`（草稿）。

        ### 各類型必填段落

        | 類型 | 必填 | 規則 |
        | --- | --- | --- |
        | Progress Note | S／O／A／P 四段 | BR-DOC-001 |
        | Admission Note | CC／PI／PH／入院生命徵象／初步評估 | BR-DOC-002 |
        | Discharge Summary | 入出院日期與診斷、檢驗檢查、手術處置、已執行醫囑、住院經過、追蹤計畫、轉診建議 | BR-DOC-003 |

        ### 自動帶入的約束

        - 自動帶入的內容一律保留來源編號（`section.entry` 用**帶版本**的 Reference，BR-DOC-008），
          這樣「引用當時看到的數值」不會被後來的更正改掉。
        - 複製前一日病程須帶 `copyTrace`，記錄複製來源與**哪些 SOAP 段落被改過**。
          **複製不視為已核對**（BR-DOC-007）。
        - 草稿依作者、病人、文件類型分開儲存，切換類型不互相覆寫（BR-DOC-010）。
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/Composition' }
      responses:
        '201': { $ref: '#/components/responses/Created' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  /Composition/{id}:
    parameters:
      - $ref: '#/components/parameters/id'
    get:
      tags: [病歷文件]
      summary: 讀取病歷文件
      operationId: readComposition
      responses:
        '200':
          description: 病歷文件
          headers:
            ETag: { $ref: '#/components/headers/ETag' }
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/Composition' }
        '404': { $ref: '#/components/responses/NotFound' }
    put:
      tags: [病歷文件]
      summary: 更新病歷文件
      operationId: updateComposition
      description: |
        | 狀態 | 可改 |
        | --- | --- |
        | `preliminary`（草稿） | 全部欄位 |
        | `final`（已簽） | **無**。更正一律新建 Composition ＋ `relatesTo[replaces]` ＋ 更正原因必填 |
        | `amended` | 無 |

        更正是**新增**不是修改：舊版轉 `amended`，內容完整保留（BR-DOC-005）。
        理由：`_history` 是技術稽核軌跡，臨床閱讀者需要在當前資源集合裡就看到「這份有更正版」。
      parameters:
        - $ref: '#/components/parameters/ifMatch'
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/Composition' }
      responses:
        '200': { $ref: '#/components/responses/Updated' }
        '409':
          description: 已簽文件不可修改
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/OperationOutcome' }
              example:
                resourceType: OperationOutcome
                issue:
                  - severity: error
                    code: conflict
                    details:
                      coding:
                        - system: http://icu.emr.local/CodeSystem/api-error
                          code: SIGNED-IMMUTABLE
                    diagnostics: 已簽署的文件不可修改。請建立更正版本並填寫更正原因，原文會完整保留。
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  /Composition/{id}/$sign:
    parameters:
      - $ref: '#/components/parameters/id'
    post:
      tags: [病歷文件]
      summary: 簽署文件
      operationId: signComposition
      description: |
        只能簽自己的 `preliminary` 文件（BR-DOC-004）。
        簽署後 `status` 轉 `final` 並建立 `Provenance.signature`。

        簽章驗證結果由伺服器產生，透過 `extension[signatureValidation]` 回傳：
        `valid` / `invalid` / `unverified`。憑證服務不可用時用 `unverified`，**不預設 valid**。
      parameters:
        - $ref: '#/components/parameters/ifMatch'
      responses:
        '200':
          description: 已簽署，回傳更新後的文件與 Provenance
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/Bundle' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }

  /Composition/{id}/$receive:
    parameters:
      - $ref: '#/components/parameters/id'
    post:
      tags: [工作與交班]
      summary: 接收交班
      operationId: receiveHandover
      description: |
        交班版本是 `Composition`（SBAR），接收是 `Provenance`。本操作原子完成：

        1. 建立 `Provenance`（`activity = handover-receive`）。
        2. 把交出者名下**未完成**的 Task `owner` 轉移給接收者（已完成的不動，BR-HO-004）。

        ### 拒絕條件

        | 規則 | 條件 | 錯誤碼 |
        | --- | --- | --- |
        | BR-HO-003 | 接收者角色與交班角色不符 | `HANDOVER-ROLE-MISMATCH` |
        | BR-HO-005 | 該版本已被更正 | `HANDOVER-SUPERSEDED` |
        | BR-HO-006 | 同一版本重複接收 | `HANDOVER-ALREADY-RECEIVED` |

        交班內容含交出當下未結工作的**快照**，快照不隨後續變動而改（BR-HO-007）。
      responses:
        '200':
          description: 已接收，回傳 Provenance 與被轉移的 Task
          content:
            application/fhir+json:
              schema: { $ref: '#/components/schemas/Bundle' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }

  # ────────────────────────── 工作與交班 ──────────────────────────
  /Task:
    get:
      tags: [工作與交班]
      summary: 搜尋臨床工作
      operationId: searchTask
      parameters:
        - $ref: '#/components/parameters/patient'
        - { name: status, in: query, schema: { type: string, enum: [draft, requested, received, accepted, rejected, ready, cancelled, in-progress, on-hold, failed, completed, entered-in-error] } }
        - { name: owner, in: query, schema: { type: string } }
        - { name: focus, in: query, schema: { type: string }, description: 來源單據（報告、醫令、文件） }
        - { name: business-status, in: query, schema: { type: string } }
        - $ref: '#/components/parameters/count'
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }
    post:
      tags: [工作與交班]
      summary: 建立臨床工作
      operationId: createTask
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/Task' }
      responses:
        '201': { $ref: '#/components/responses/Created' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  /Task/{id}:
    parameters:
      - $ref: '#/components/parameters/id'
    put:
      tags: [工作與交班]
      summary: 更新工作狀態
      operationId: updateTask
      description: |
        | 規則 | 內容 |
        | --- | --- |
        | BR-TASK-001 | 只有目前 `owner` 可更新 |
        | BR-TASK-002 | 結案必填完成證據（`output`）；若 `focus` 是檢驗報告，該報告必須**已回報且已有判讀紀錄** |
        | BR-TASK-003 | 狀態異動一律追加歷程，不覆寫 |

        BR-TASK-002 的用意：待回結果尚未完成處理就不能結案，避免工作被形式上關掉。

        原型狀態對映：`open`→`requested`、`accepted`→`accepted`、`waiting`→`on-hold`、`done`→`completed`。
      parameters:
        - $ref: '#/components/parameters/ifMatch'
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/Task' }
      responses:
        '200': { $ref: '#/components/responses/Updated' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
        '412': { $ref: '#/components/responses/PreconditionFailed' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  # ────────────────────────── 溯源與稽核 ──────────────────────────
  /Provenance:
    get:
      tags: [溯源與稽核]
      summary: 搜尋溯源紀錄
      operationId: searchProvenance
      parameters:
        - { name: target, in: query, schema: { type: string }, description: 被溯源的資源 }
        - { name: agent, in: query, schema: { type: string } }
        - { name: recorded, in: query, schema: { type: string } }
        - { name: activity, in: query, schema: { type: string } }
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }
    post:
      tags: [溯源與稽核]
      summary: 建立溯源紀錄
      operationId: createProvenance
      description: |
        **僅新增，永不修改。**

        ### activity 值域

        | code | 用途 | 系統 |
        | --- | --- | --- |
        | `READ` | 記錄已閱（真正的讀取動作） | `v3-DataOperation` |
        | `clinical-review` | 判讀結果／核對護理執行 | 本地 |
        | `handover-receive` | 交班接收 | 本地 |
        | `roster-confirm` | 照護名單確認 | 本地 |
        | `sign` | 簽署 | 本地 |

        「判讀」「核對」「交班接收」「名單確認」是**臨床業務動作，不是資料操作**，
        `v3-DataOperation` 沒有對應碼（該系統只有 OPERATE／CREATE／READ／UPDATE／DELETE／APPEND／MODIFYSTATUS 等），
        因此改用本地 CodeSystem，不硬套語義不符的標準碼。

        ### 相關規則

        - BR-MED-008／BR-RPT-003：`clinical-review` 的 `reviewNote` 必填，前次核對結論全部保留。
        - BR-RPT-002：記錄已閱／判讀時，報告必須已回報且來源未缺漏。
        - BR-RPT-004：**已閱、判讀、工作結案彼此獨立**；任一項完成不影響其他待回項目。
      requestBody:
        required: true
        content:
          application/fhir+json:
            schema: { $ref: '#/components/schemas/Provenance' }
            examples:
              executionReview:
                $ref: '#/components/examples/ProvenanceExecutionReview'
      responses:
        '201': { $ref: '#/components/responses/Created' }
        '403': { $ref: '#/components/responses/Forbidden' }
        '409': { $ref: '#/components/responses/Conflict' }
        '422': { $ref: '#/components/responses/BusinessRuleViolation' }

  /AuditEvent:
    get:
      tags: [溯源與稽核]
      summary: 查詢稽核軌跡（限稽核角色）
      operationId: searchAuditEvent
      description: |
        伺服器自動產生，每個請求一筆，**包含讀取**（`action=R`）。失敗的請求同樣記錄（尤其 403）。

        `agent.network.address` 由伺服器從連線取得，客戶端送的值忽略——
        對應規格中 `ipAddress`／`computerName`「不可手打冒充」的要求。

        臨床帳號不可查詢此端點。
      parameters:
        - $ref: '#/components/parameters/patient'
        - { name: agent, in: query, schema: { type: string } }
        - { name: date, in: query, schema: { type: string } }
        - { name: action, in: query, schema: { type: string, enum: [C, R, U, D, E] } }
        - { name: outcome, in: query, schema: { type: string } }
        - $ref: '#/components/parameters/count'
      responses:
        '200': { $ref: '#/components/responses/SearchSet' }
        '403': { $ref: '#/components/responses/Forbidden' }

  # ──────────────────────────── 目錄 ────────────────────────────
  /ValueSet/{valueSetId}/$expand:
    parameters:
      - name: valueSetId
        in: path
        required: true
        schema:
          type: string
          enum: [his-medication, his-service, his-route, his-unit, icd-10, diet, supply]
    get:
      tags: [目錄]
      summary: 展開 B 目錄品項
      operationId: expandValueSet
      description: |
        B 目錄共 31,146 筆（藥品 2,935／服務 7,892／ICD-10 15,994／途徑 62／單位 216／飲食 285／耗材 3,752）。
        品項查詢走 `$expand`，**不走資源搜尋**。

        - `ValueSet.version` 帶目錄版本（例：`20260831-b1`）。
        - 回應帶 `Cache-Control: max-age=3600`。
        - **中文譯名未核定**：`display` 一律填來源原文（越文／英文），
          譯名放 `extension[reviewTranslation]`，避免未核定譯名被當正式名稱往下游傳。
        - 停用品項（`active=false`）會回傳但標記為 inactive，不得用於新開立（BR-CAT-001）。
      parameters:
        - { name: filter, in: query, schema: { type: string }, description: 關鍵字，支援代碼、越文、英文模糊比對 }
        - { name: count, in: query, schema: { type: integer, default: 20, maximum: 200 } }
        - { name: offset, in: query, schema: { type: integer, default: 0 } }
      responses:
        '200':
          description: 展開結果
          headers:
            Cache-Control:
              schema: { type: string }
              example: max-age=3600
          content:
            application/fhir+json:
              schema:
                type: object
                additionalProperties: true

components:

  securitySchemes:
    smartOnFhir:
      type: oauth2
      description: |
        OAuth 2.0 ＋ SMART on FHIR。存取權杖必須帶：

        | Claim | 內容 |
        | --- | --- |
        | `sub` | 使用者識別 |
        | `fhirUser` | `PractitionerRole/{id}`，決定角色與科別 |
        | `shift` | 班別代碼，決定「本班」時間窗與交班對象 |

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

        除 scope 外，伺服器對每個請求另做病人範圍（compartment）檢查。
      flows:
        authorizationCode:
          authorizationUrl: https://auth.icu.local/authorize
          tokenUrl: https://auth.icu.local/token
          scopes:
            user/Patient.rs: 讀取與搜尋病人
            user/Encounter.rs: 讀取與搜尋就醫事件
            user/MedicationRequest.cru: 開立與維護藥物醫囑（主責醫師）
            user/MedicationRequest.rs: 讀取藥物醫囑（護理師）
            user/MedicationAdministration.cs: 記錄給藥執行（護理師）
            user/ServiceRequest.cru: 開立與維護醫令（主責醫師）
            user/Observation.crs: 記錄與讀取量測（護理師）
            user/Observation.rs: 讀取量測（醫師）
            user/DiagnosticReport.rs: 讀取檢驗報告
            user/Composition.cru: 撰寫與維護病歷文件
            user/Composition.rs: 唯讀病歷（照會醫師）
            user/Task.cru: 臨床工作
            user/Provenance.cs: 建立溯源紀錄
            user/Communication.cs: 必要溝通
            user/Condition.cru: 診斷管理（主責醫師）
            user/Goal.cru: 查房問題與目標（主責醫師）
            user/CarePlan.cs: 照顧重點
            system/DiagnosticReport.cu: 整合服務落地檢驗結果
            system/Patient.cu: 整合服務同步病人
            system/Encounter.cu: 整合服務同步就醫事件

  parameters:
    id:
      name: id
      in: path
      required: true
      description: 資源的 EMR 內部主鍵。**絕不等於任何 HIS 號碼。**
      schema: { type: string }
    patient:
      name: patient
      in: query
      description: 病人 Reference，例 `Patient/abc`
      schema: { type: string }
    encounter:
      name: encounter
      in: query
      schema: { type: string }
    count:
      name: _count
      in: query
      description: 每頁筆數。分頁一律跟隨 `Bundle.link[next]` 的 URL，不得自行拼 offset。
      schema: { type: integer, default: 20, maximum: 200 }
    sort:
      name: _sort
      in: query
      schema: { type: string }
    include:
      name: _include
      in: query
      description: 一併帶回被引用的資源，減少往返
      schema: { type: string }
    revinclude:
      name: _revinclude
      in: query
      description: 一併帶回反向引用本資源的資源
      schema: { type: string }
    ifMatch:
      name: If-Match
      in: header
      required: true
      description: |
        樂觀鎖，值為讀取時取得的 `ETag`（例 `W/"4"`）。
        **缺少時直接回 `412`**，不允許盲目覆寫。
      schema: { type: string }
      example: W/"4"
    ifNoneExist:
      name: If-None-Exist
      in: header
      description: |
        條件式建立。符合條件的資源已存在時回 `200`（回傳既有資源）而非 `201`。

        給藥劑次唯一性用：
        `identifier=http://icu.emr.local/CodeSystem/occurrence-id|M1-V1-D1&status=completed`
      schema: { type: string }

  headers:
    ETag:
      description: 資源版本，用於後續 `If-Match`
      schema: { type: string }
      example: W/"4"
    Location:
      description: 新建資源的 URL
      schema: { type: string }

  responses:
    SearchSet:
      description: 搜尋結果（`Bundle.type = searchset`）
      content:
        application/fhir+json:
          schema: { $ref: '#/components/schemas/Bundle' }
    Created:
      description: 已建立
      headers:
        Location: { $ref: '#/components/headers/Location' }
        ETag: { $ref: '#/components/headers/ETag' }
      content:
        application/fhir+json:
          schema:
            type: object
            additionalProperties: true
    Updated:
      description: 已更新
      headers:
        ETag: { $ref: '#/components/headers/ETag' }
      content:
        application/fhir+json:
          schema:
            type: object
            additionalProperties: true
    BadRequest:
      description: 語法錯誤（JSON 壞掉、參數不合法）
      content:
        application/fhir+json:
          schema: { $ref: '#/components/schemas/OperationOutcome' }
    Unauthorized:
      description: 未認證或權杖失效
      content:
        application/fhir+json:
          schema: { $ref: '#/components/schemas/OperationOutcome' }
    Forbidden:
      description: |
        已認證但無權（角色限制、病人不在照護範圍）。

        **不因無權而回 404**——權限問題一律 403，資源不存在才 404。
        但搜尋是例外：一律回較少的結果而非 403，避免探測病人是否存在。
      content:
        application/fhir+json:
          schema: { $ref: '#/components/schemas/OperationOutcome' }
    NotFound:
      description: 資源不存在
      content:
        application/fhir+json:
          schema: { $ref: '#/components/schemas/OperationOutcome' }
    Conflict:
      description: |
        版本衝突，或目前狀態不允許此操作。

        版本衝突時必須帶目前版本，讓客戶端就地合併——**內容不會被覆寫**。
      content:
        application/fhir+json:
          schema: { $ref: '#/components/schemas/OperationOutcome' }
          example:
            resourceType: OperationOutcome
            issue:
              - severity: error
                code: conflict
                details: { text: currentVersionId=6 }
                diagnostics: 資源已被其他工作階段更新至 versionId=6。請重新讀取後合併，內容未被覆寫。
    PreconditionFailed:
      description: 缺少 `If-Match` 標頭
      content:
        application/fhir+json:
          schema: { $ref: '#/components/schemas/OperationOutcome' }
    BusinessRuleViolation:
      description: |
        資源結構合法但違反業務規則。`issue.details.coding.code` 是本院錯誤碼，
        `issue.diagnostics` 是**可直接顯示給臨床使用者的繁中訊息**。
      content:
        application/fhir+json:
          schema: { $ref: '#/components/schemas/OperationOutcome' }
    HisUnavailable:
      description: HIS 不可用
      content:
        application/fhir+json:
          schema: { $ref: '#/components/schemas/OperationOutcome' }
    HisTimeout:
      description: |
        HIS 逾時。本地醫囑仍持久化為已送出，`hisTransmissionStatus=unknown`，
        並產生人工查明 Task。**之後禁止自動重送。**
      content:
        application/fhir+json:
          schema: { $ref: '#/components/schemas/OperationOutcome' }

  schemas:

    # ── 基礎型別（僅列本專案用到的元素） ──
    Coding:
      type: object
      additionalProperties: true
      properties:
        system: { type: string }
        code: { type: string }
        display: { type: string, description: 'B 目錄品項一律填來源原文；中文譯名未核定，放 extension' }

    CodeableConcept:
      type: object
      additionalProperties: true
      properties:
        coding:
          type: array
          items: { $ref: '#/components/schemas/Coding' }
        text: { type: string }

    Identifier:
      type: object
      additionalProperties: true
      properties:
        use: { type: string }
        system: { type: string }
        value: { type: string }

    Reference:
      type: object
      additionalProperties: true
      properties:
        reference:
          type: string
          description: |
            資源參照。引用臨床來源時**必須帶版本**（例 `Composition/abc/_history/2`），
            這樣引用當時看到的內容不會被後來的更正改掉。
          example: MedicationRequest/8f3a/_history/3
        display: { type: string }

    Quantity:
      type: object
      additionalProperties: true
      properties:
        value: { type: number }
        unit: { type: string }
        system: { type: string }
        code: { type: string }

    Meta:
      type: object
      additionalProperties: true
      properties:
        versionId: { type: string }
        lastUpdated: { type: string, format: date-time }

    Extension:
      type: object
      additionalProperties: true
      properties:
        url: { type: string }

    # ── 錯誤 ──
    ApiErrorCode:
      type: string
      description: 本院錯誤碼（`system = http://icu.emr.local/CodeSystem/api-error`）
      enum:
        - UNKNOWN-ACTOR
        - ROLE-DOCTOR-ONLY
        - ROLE-NURSE-ONLY
        - CONSULT-READ-ONLY
        - ACTOR-NOT-ASSIGNABLE
        - PATIENT-REQUIRED
        - PATIENT-OUT-OF-SCOPE
        - ENCOUNTER-MISMATCH
        - ROSTER-NOT-CONFIRMED
        - SOURCE-PATIENT-MISMATCH
        - READ-ONLY-SOURCE
        - PROGRESS-NOTE-REQUIRED
        - SIGN-OWN-DRAFT-ONLY
        - SUBMIT-STATE-INVALID
        - HIS-RESPONSE-INVALID
        - ORDER-IMMUTABLE-AFTER-SUBMIT
        - RESEND-BLOCKED-UNKNOWN
        - CODE-REQUIRED
        - ORDER-NOT-EXECUTABLE
        - FIVE-RIGHTS-INCOMPLETE
        - VARIANCE-REASON-REQUIRED
        - DOSE-ALREADY-ADMINISTERED
        - TIME-OUT-OF-ORDER-PERIOD
        - ACTUAL-VALUE-REQUIRED
        - REVIEW-NOTE-REQUIRED
        - VALUE-INVALID
        - BP-COMPONENTS-REQUIRED
        - ZERO-AS-MISSING
        - INFUSION-SOURCE-REQUIRED
        - REPORT-NOT-AVAILABLE
        - SECTION-REQUIRED
        - SIGNED-IMMUTABLE
        - AMEND-REASON-REQUIRED
        - NO-COPYABLE-PROGRESS
        - CATALOG-INACTIVE
        - CATALOG-CHANGED
        - TASK-NOT-OWNER
        - EVIDENCE-REQUIRED
        - SOURCE-NOT-PROCESSED
        - SUMMARY-REQUIRED
        - HANDOVER-SUPERSEDED
        - HANDOVER-ROLE-MISMATCH
        - HANDOVER-ALREADY-RECEIVED
        - ASSIGNEE-INVALID
        - CONSULT-NOT-ASSIGNEE
        - CONSULT-ALREADY-ANSWERED
        - RECOMMENDATION-REQUIRED
        - TIMEZONE-REQUIRED
        - TIME-INVALID
        - DISPENSE-STOCK-UNMAPPED
        - TRISTATE-NOT-RESOLVED
        - DOSE-UNIT-INCONSISTENT
        - ARCHIVE-ID-STALE
        - PERFORMING-PLACE-UNMAPPED
        - PRICE-TYPE-MISSING
        - SPECIMEN-CODE-UNMAPPED
        - PATIENT-TYPE-INVALID
        - HIS-UNAVAILABLE
        - HIS-TIMEOUT

    OperationOutcome:
      type: object
      required: [resourceType, issue]
      additionalProperties: true
      properties:
        resourceType: { type: string, enum: [OperationOutcome] }
        issue:
          type: array
          minItems: 1
          items:
            type: object
            required: [severity, code, details, diagnostics]
            properties:
              severity: { type: string, enum: [fatal, error, warning, information] }
              code:
                type: string
                description: FHIR R4 標準 issue-type
                enum: [invalid, structure, required, value, invariant, security, login, forbidden, processing, not-supported, duplicate, not-found, conflict, transient, timeout, business-rule, exception, throttled, informational]
              details:
                type: object
                properties:
                  coding:
                    type: array
                    items:
                      type: object
                      properties:
                        system: { type: string, example: http://icu.emr.local/CodeSystem/api-error }
                        code: { $ref: '#/components/schemas/ApiErrorCode' }
                  text: { type: string }
              diagnostics:
                type: string
                description: '**給臨床使用者看的繁中訊息**，可直接顯示在畫面。不是給工程師的堆疊訊息。'
              expression:
                type: array
                description: 欄位層級錯誤時必填，FHIRPath 路徑
                items: { type: string }

    JsonPatch:
      type: array
      items:
        type: object
        required: [op, path]
        properties:
          op: { type: string, enum: [add, remove, replace, move, copy, test] }
          path: { type: string }
          value: {}

    Bundle:
      type: object
      required: [resourceType, type]
      additionalProperties: true
      properties:
        resourceType: { type: string, enum: [Bundle] }
        type:
          type: string
          enum: [searchset, transaction, transaction-response, batch, batch-response, history, collection]
        total: { type: integer }
        link:
          type: array
          description: 分頁連結。客戶端一律跟隨 `next`，不得自行拼 offset。
          items:
            type: object
            properties:
              relation: { type: string, enum: [self, next, previous, first, last] }
              url: { type: string }
        entry:
          type: array
          items:
            type: object
            properties:
              fullUrl: { type: string }
              resource: { type: object, additionalProperties: true }
              search:
                type: object
                properties:
                  mode: { type: string, enum: [match, include, outcome] }
              request:
                type: object
                description: transaction／batch 必填
                properties:
                  method: { type: string, enum: [GET, POST, PUT, PATCH, DELETE] }
                  url: { type: string }
                  ifMatch: { type: string }
                  ifNoneExist: { type: string }
              response:
                type: object
                properties:
                  status: { type: string }
                  location: { type: string }
                  etag: { type: string }

    # ── 資源（僅列本專案約束的元素） ──
    Patient:
      type: object
      required: [resourceType]
      additionalProperties: true
      description: |
        **HIS 唯讀。** 臨床帳號寫入一律回 `422 READ-ONLY-SOURCE`。

        年齡不落地——由客戶端從 `birthDate` 於檢視時點計算。
      properties:
        resourceType: { type: string, enum: [Patient] }
        id: { type: string }
        meta: { $ref: '#/components/schemas/Meta' }
        identifier:
          type: array
          items: { $ref: '#/components/schemas/Identifier' }
        name: { type: array, items: { type: object, additionalProperties: true } }
        gender: { type: string, enum: [male, female, other, unknown] }
        birthDate: { type: string, format: date }

    CareTeam:
      type: object
      required: [resourceType, status]
      additionalProperties: true
      properties:
        resourceType: { type: string, enum: [CareTeam] }
        id: { type: string }
        status: { type: string, enum: [proposed, active, suspended, inactive, entered-in-error] }
        subject: { $ref: '#/components/schemas/Reference' }
        encounter: { $ref: '#/components/schemas/Reference' }
        participant:
          type: array
          items:
            type: object
            additionalProperties: true
            properties:
              member: { $ref: '#/components/schemas/Reference' }
              role:
                type: array
                items: { $ref: '#/components/schemas/CodeableConcept' }
        extension:
          type: array
          items: { $ref: '#/components/schemas/Extension' }

    MedicationRequest:
      type: object
      required: [resourceType, status, intent, subject]
      additionalProperties: true
      description: |
        ### 三軸狀態

        | 軸 | 表達 |
        | --- | --- |
        | 臨床狀態 | `status`：`draft` → `active` → `stopped`／`cancelled` |
        | 本地簽署 | 存在指向該版本的 `Provenance.signature` |
        | HIS 傳輸 | `extension[hisTransmissionStatus]` |

        **只有 `status=active` ＋ `hisTransmissionStatus=accepted` 才是護理端可執行狀態。**
      properties:
        resourceType: { type: string, enum: [MedicationRequest] }
        id: { type: string }
        meta: { $ref: '#/components/schemas/Meta' }
        extension:
          type: array
          description: |
            本地擴充：

            | url 尾段 | 型別 | 用途 |
            | --- | --- | --- |
            | `hisTransmissionStatus` | code | `notSent`／`sent`／`accepted`／`rejected`／`unknown` |
            | `hisTransmissionEvent` | complex | `requestId`、`sentAt`、`respondedAt`、`message` |
            | `catalogSnapshot` | complex | 開立當下的目錄品項快照（`catalogVersion`、`sourceSHA256`） |
            | `orderSet` | Identifier | 套組來源 |
            | `doseSafetyDisplay` | code | 劑量安全**畫面示例**的選擇結果，非臨床判定 |
          items: { $ref: '#/components/schemas/Extension' }
        identifier:
          type: array
          description: 'HIS 處方號（`his-prescription-number`）與每行藥品碼（`his-medication-request-id`）。取消處方必須用**處方號**，不是每行碼。'
          items: { $ref: '#/components/schemas/Identifier' }
        status:
          type: string
          enum: [draft, active, on-hold, cancelled, completed, stopped, entered-in-error, unknown]
        intent: { type: string, enum: [proposal, plan, order, original-order] }
        medicationCodeableConcept:
          allOf: [{ $ref: '#/components/schemas/CodeableConcept' }]
          description: '必須帶 `coding.system` ＋ `code`，且來自已同步目錄。只給 `text` 回 422（BR-MED-001）。'
        subject: { $ref: '#/components/schemas/Reference' }
        encounter: { $ref: '#/components/schemas/Reference' }
        authoredOn: { type: string, format: date-time, description: 必須帶時區位移 }
        requester:
          allOf: [{ $ref: '#/components/schemas/Reference' }]
          description: 一律取自存取權杖，客戶端送的值被忽略（BR-AUTH-006）
        supportingInformation:
          type: array
          description: 病程來源，簽署時寫入**帶版本**的 Reference（BR-ORD-003）
          items: { $ref: '#/components/schemas/Reference' }
        dosageInstruction:
          type: array
          items:
            type: object
            additionalProperties: true
            properties:
              text: { type: string }
              timing: { type: object, additionalProperties: true }
              route:
                allOf: [{ $ref: '#/components/schemas/CodeableConcept' }]
                description: 須與 HIS 藥品目錄一致，**不能用本地 PO/IV 假定等值**
              doseAndRate:
                type: array
                items:
                  type: object
                  additionalProperties: true
                  properties:
                    doseQuantity: { $ref: '#/components/schemas/Quantity' }
                    rateQuantity:
                      allOf: [{ $ref: '#/components/schemas/Quantity' }]
                      description: 需單位與濃度，**不把 mg/kg/min 直接當 mL/h**

    MedicationAdministration:
      type: object
      required: [resourceType, status, subject, effectiveDateTime]
      additionalProperties: true
      description: '**建立後不可修改。** 沒有紀錄 ≠ `not-done`——沒建立資源就是「尚未記錄」。'
      properties:
        resourceType: { type: string, enum: [MedicationAdministration] }
        id: { type: string }
        meta: { $ref: '#/components/schemas/Meta' }
        extension:
          type: array
          description: |
            `verificationChecklist`（五對人工核對）：
            `rightPatient`／`rightMedication`／`rightDose`／`rightRoute`／`rightTime` 五個 boolean ＋ `checkedBy`。
            `status=completed` 時五項必須全為 `true`（BR-MED-003）。
          items: { $ref: '#/components/schemas/Extension' }
        identifier:
          type: array
          description: 劑次識別碼（`occurrence-id`），用於 `If-None-Exist` 保證同劑次至多一筆完成紀錄
          items: { $ref: '#/components/schemas/Identifier' }
        status:
          type: string
          enum: [in-progress, not-done, on-hold, completed, entered-in-error, stopped, unknown]
          description: '原型對映：`done`→`completed`、`held`→`on-hold`、`notDone`→`not-done`'
        statusReason:
          type: array
          description: '`not-done` 時必填'
          items: { $ref: '#/components/schemas/CodeableConcept' }
        medicationCodeableConcept: { $ref: '#/components/schemas/CodeableConcept' }
        subject: { $ref: '#/components/schemas/Reference' }
        context: { $ref: '#/components/schemas/Reference' }
        effectiveDateTime:
          type: string
          format: date-time
          description: 實際執行時間（護理師填）。與伺服器產生的記錄時間分開儲存（BR-TIME-003）
        performer:
          type: array
          items: { type: object, additionalProperties: true }
        request:
          allOf: [{ $ref: '#/components/schemas/Reference' }]
          description: 來源醫囑，**帶版本**
        dosage:
          type: object
          additionalProperties: true
          description: '`completed` 時 `dose` 與 `route` 必填，不得以醫囑預定值當事實（BR-MED-007）'
          properties:
            text: { type: string }
            route: { $ref: '#/components/schemas/CodeableConcept' }
            dose: { $ref: '#/components/schemas/Quantity' }
            rateQuantity: { $ref: '#/components/schemas/Quantity' }
        note:
          type: array
          description: 實際值與醫囑不同、或狀態非 `completed` 時必填（BR-MED-004）
          items: { type: object, additionalProperties: true }

    ServiceRequest:
      type: object
      required: [resourceType, status, intent, subject]
      additionalProperties: true
      properties:
        resourceType: { type: string, enum: [ServiceRequest] }
        id: { type: string }
        meta: { $ref: '#/components/schemas/Meta' }
        identifier:
          type: array
          description: '`order-number`（整張需求單）與 `service-request-number`（單內一行）。**兩者不可互相代入。**'
          items: { $ref: '#/components/schemas/Identifier' }
        status: { type: string, enum: [draft, active, on-hold, revoked, completed, entered-in-error, unknown] }
        intent: { type: string, enum: [proposal, plan, order, original-order] }
        category:
          type: array
          description: 跨科照會用 `consultation`
          items: { $ref: '#/components/schemas/CodeableConcept' }
        priority:
          type: string
          enum: [routine, urgent, asap, stat]
          description: 送 HIS 建立時對映為 severity 1／2／3；**修改時不送**（Q-05）
        code: { $ref: '#/components/schemas/CodeableConcept' }
        subject: { $ref: '#/components/schemas/Reference' }
        encounter: { $ref: '#/components/schemas/Reference' }
        authoredOn: { type: string, format: date-time }
        requester: { $ref: '#/components/schemas/Reference' }
        performer:
          type: array
          description: 跨科照會的受照會醫師
          items: { $ref: '#/components/schemas/Reference' }
        specimen:
          type: array
          items: { $ref: '#/components/schemas/Reference' }
        bodySite:
          type: array
          description: 採樣／拍攝部位。**不混用檢體類型。**
          items: { $ref: '#/components/schemas/CodeableConcept' }

    Procedure:
      type: object
      required: [resourceType, status, subject]
      additionalProperties: true
      properties:
        resourceType: { type: string, enum: [Procedure] }
        id: { type: string }
        status: { type: string, enum: [preparation, in-progress, not-done, on-hold, stopped, completed, entered-in-error, unknown] }
        basedOn:
          type: array
          items: { $ref: '#/components/schemas/Reference' }
        subject: { $ref: '#/components/schemas/Reference' }
        encounter: { $ref: '#/components/schemas/Reference' }
        performedDateTime: { type: string, format: date-time }
        performer: { type: array, items: { type: object, additionalProperties: true } }
        statusReason: { $ref: '#/components/schemas/CodeableConcept' }

    DiagnosticReport:
      type: object
      required: [resourceType, status, code]
      additionalProperties: true
      description: |
        **HIS 唯讀。** 每個檢驗指標必須是獨立的 `Observation`（由 `result` 引用），
        不可塞進 `conclusion` 的文字——趨勢圖需要逐指標時間序列。

        無關聯醫囑時 `basedOn` 留空並在 UI 顯示「未提供來源醫囑關聯」，
        **不按名稱猜測配對**（名稱相近就自動綁定會產生錯誤的醫囑－結果對應）。
      properties:
        resourceType: { type: string, enum: [DiagnosticReport] }
        id: { type: string }
        identifier: { type: array, items: { $ref: '#/components/schemas/Identifier' } }
        status:
          type: string
          enum: [registered, partial, preliminary, final, amended, corrected, appended, cancelled, entered-in-error, unknown]
          description: '`registered` = 待回。醫囑被 HIS 接受時自動建立（BR-ORD-008）。重發用 `corrected`，舊版保留。'
        code: { $ref: '#/components/schemas/CodeableConcept' }
        subject: { $ref: '#/components/schemas/Reference' }
        encounter: { $ref: '#/components/schemas/Reference' }
        basedOn: { type: array, items: { $ref: '#/components/schemas/Reference' } }
        issued: { type: string, format: date-time }
        specimen: { type: array, items: { $ref: '#/components/schemas/Reference' } }
        result:
          type: array
          description: 逐項檢驗指標
          items: { $ref: '#/components/schemas/Reference' }
        conclusion: { type: string, description: 待回時**不填**，不可預設「正常」 }

    Observation:
      type: object
      required: [resourceType, status, code]
      additionalProperties: true
      description: |
        **建立後不可修改。**

        血壓必須用單一 Observation 的兩個 `component`（收縮 8480-6／TDL.0094、
        舒張 8462-4／TDL.0095），拆成兩筆會失去配對。

        已確認可用的 LOINC：心率 `8867-4`、體溫 `8310-5`、呼吸頻率 `9279-1`、
        SpO₂ `59408-5`、體重 `29463-7`。
        **檢驗指標字典（Q-02）與出入量項目（Q-03）在來源查不到對照，
        只帶院內／TDL 碼，不自行指派 LOINC。**
      properties:
        resourceType: { type: string, enum: [Observation] }
        id: { type: string }
        extension:
          type: array
          description: '`notCountedAsIntake`（boolean）：輸注速率觀察不列入出入量彙總'
          items: { $ref: '#/components/schemas/Extension' }
        status: { type: string, enum: [registered, preliminary, final, amended, corrected, cancelled, entered-in-error, unknown] }
        category: { type: array, items: { $ref: '#/components/schemas/CodeableConcept' } }
        code: { $ref: '#/components/schemas/CodeableConcept' }
        subject: { $ref: '#/components/schemas/Reference' }
        encounter: { $ref: '#/components/schemas/Reference' }
        effectiveDateTime: { type: string, format: date-time }
        valueQuantity: { $ref: '#/components/schemas/Quantity' }
        dataAbsentReason:
          allOf: [{ $ref: '#/components/schemas/CodeableConcept' }]
          description: |
            缺值時填此欄並**省略** `value[x]`。**禁止送 0 代替缺值**（BR-OBS-003）。
            本專案只用：`unknown`／`not-performed`／`not-asked`／`error`／`masked`。
        interpretation:
          type: array
          description: '**只有來源提供旗標時才填。無旗標 ≠ 正常**（BR-RPT-005）。'
          items: { $ref: '#/components/schemas/CodeableConcept' }
        device:
          allOf: [{ $ref: '#/components/schemas/Reference' }]
          description: 設備來源（取得＝D）時標示
        partOf: { type: array, items: { $ref: '#/components/schemas/Reference' } }
        component:
          type: array
          description: 血壓等多子項量測。子項共用同一 `effectiveDateTime`。
          items:
            type: object
            additionalProperties: true
            properties:
              code: { $ref: '#/components/schemas/CodeableConcept' }
              valueQuantity: { $ref: '#/components/schemas/Quantity' }
              dataAbsentReason: { $ref: '#/components/schemas/CodeableConcept' }

    Composition:
      type: object
      required: [resourceType, status, type, date, author, title]
      additionalProperties: true
      properties:
        resourceType: { type: string, enum: [Composition] }
        id: { type: string }
        meta: { $ref: '#/components/schemas/Meta' }
        extension:
          type: array
          description: |
            | url 尾段 | 用途 |
            | --- | --- |
            | `copyTrace` | 前一日病程複製軌跡（`from`、`copiedAt`、`changedSections[]`） |
            | `handoverReceiver` | 交班對象 |
            | `educationTemplate` | 衛教組套來源（在 `section` 上） |
          items: { $ref: '#/components/schemas/Extension' }
        status:
          type: string
          enum: [preliminary, final, amended, entered-in-error]
          description: '`preliminary`=草稿、`final`=已簽、`amended`=已被更正'
        type: { $ref: '#/components/schemas/CodeableConcept' }
        subject: { $ref: '#/components/schemas/Reference' }
        encounter: { $ref: '#/components/schemas/Reference' }
        date: { type: string, format: date-time, description: 臨床時間（病程時間），非建立時間 }
        author: { type: array, items: { $ref: '#/components/schemas/Reference' } }
        title: { type: string }
        relatesTo:
          type: array
          description: 更正鏈。更正一律新建並以 `replaces` 指向舊版，舊版內容完整保留。
          items:
            type: object
            additionalProperties: true
            properties:
              code: { type: string, enum: [replaces, transforms, signs, appends] }
              targetReference: { $ref: '#/components/schemas/Reference' }
        section:
          type: array
          items:
            type: object
            additionalProperties: true
            properties:
              title: { type: string }
              code: { $ref: '#/components/schemas/CodeableConcept' }
              text: { type: object, additionalProperties: true }
              entry:
                type: array
                description: 引用來源，**必須帶版本**（BR-DOC-008）
                items: { $ref: '#/components/schemas/Reference' }

    Task:
      type: object
      required: [resourceType, status, intent]
      additionalProperties: true
      properties:
        resourceType: { type: string, enum: [Task] }
        id: { type: string }
        status:
          type: string
          enum: [draft, requested, received, accepted, rejected, ready, cancelled, in-progress, on-hold, failed, completed, entered-in-error]
        intent: { type: string }
        description: { type: string }
        for: { $ref: '#/components/schemas/Reference' }
        encounter: { $ref: '#/components/schemas/Reference' }
        focus:
          allOf: [{ $ref: '#/components/schemas/Reference' }]
          description: 來源單據（DiagnosticReport／ServiceRequest／Composition）
        owner:
          allOf: [{ $ref: '#/components/schemas/Reference' }]
          description: 交班接收時由伺服器轉移
        output:
          type: array
          description: 完成證據。結案必填（BR-TASK-002）。
          items: { type: object, additionalProperties: true }
        note:
          type: array
          description: 承接／變更歷程，一律追加不覆寫
          items: { type: object, additionalProperties: true }

    Provenance:
      type: object
      required: [resourceType, target, recorded, agent]
      additionalProperties: true
      description: '**僅新增，永不修改。** 與 `AuditEvent` 分工：Provenance 是臨床溯源（畫面可見），AuditEvent 是安全稽核（僅稽核人員）。'
      properties:
        resourceType: { type: string, enum: [Provenance] }
        id: { type: string }
        extension:
          type: array
          description: |
            | url 尾段 | 用途 |
            | --- | --- |
            | `reviewOutcome` | `reviewed`／`difference` |
            | `reviewNote` | 核對依據／判讀處理紀錄（必填） |
            | `signatureValidation` | `valid`／`invalid`／`unverified`。**憑證服務不可用時用 `unverified`，不預設 valid** |
          items: { $ref: '#/components/schemas/Extension' }
        target: { type: array, minItems: 1, items: { $ref: '#/components/schemas/Reference' } }
        recorded: { type: string, format: date-time }
        activity: { $ref: '#/components/schemas/CodeableConcept' }
        agent:
          type: array
          minItems: 1
          items:
            type: object
            additionalProperties: true
            properties:
              type: { $ref: '#/components/schemas/CodeableConcept' }
              who: { $ref: '#/components/schemas/Reference' }
        entity: { type: array, items: { type: object, additionalProperties: true } }
        signature:
          type: array
          description: 電子簽章。`signature_hash` 與驗證結果由伺服器產生，客戶端不可送。
          items: { type: object, additionalProperties: true }

  examples:
    MedicationRequestActive:
      summary: 生效中的藥物醫囑
      value:
        resourceType: MedicationRequest
        id: mr-efferalgan-0831
        meta: { versionId: '3', lastUpdated: '2026-08-31T08:05:12+07:00' }
        extension:
          - url: http://icu.emr.local/StructureDefinition/hisTransmissionStatus
            valueCode: accepted
        identifier:
          - system: http://icu.emr.local/CodeSystem/his-prescription-number
            value: HP-2026083100417
        status: active
        intent: order
        medicationCodeableConcept:
          coding:
            - system: http://icu.emr.local/CodeSystem/his-medication
              code: '50631'
              display: Efferalgan 300 mg (Paracetamol)
        subject: { reference: Patient/demo-a }
        authoredOn: '2026-08-31T08:05:00+07:00'
        requester: { reference: PractitionerRole/doctor-d1 }
        supportingInformation:
          - reference: Composition/demo-soap-0831/_history/1
        dosageInstruction:
          - text: 300 mg（1 錠），口服，BID（每日 2 次）
            route:
              coding:
                - system: http://icu.emr.local/CodeSystem/his-route
                  code: '913'
                  display: Uống
            doseAndRate:
              - doseQuantity: { value: 300, unit: mg, system: http://unitsofmeasure.org, code: mg }

    MedicationAdministrationCompleted:
      summary: 已給藥（五對齊全）
      value:
        resourceType: MedicationAdministration
        extension:
          - url: http://icu.emr.local/StructureDefinition/verificationChecklist
            extension:
              - { url: rightPatient, valueBoolean: true }
              - { url: rightMedication, valueBoolean: true }
              - { url: rightDose, valueBoolean: true }
              - { url: rightRoute, valueBoolean: true }
              - { url: rightTime, valueBoolean: true }
              - { url: checkedBy, valueReference: { reference: PractitionerRole/nurse-n1 } }
        identifier:
          - system: http://icu.emr.local/CodeSystem/occurrence-id
            value: M3-V1-D1
        status: completed
        medicationCodeableConcept:
          coding:
            - system: http://icu.emr.local/CodeSystem/his-medication
              code: '50631'
        subject: { reference: Patient/demo-a }
        context: { reference: Encounter/enc-a }
        effectiveDateTime: '2026-08-31T09:00:00+07:00'
        performer:
          - actor: { reference: PractitionerRole/nurse-n1 }
        request: { reference: MedicationRequest/mr-efferalgan-0831/_history/3 }
        dosage:
          text: 300 mg（1 錠）
          route:
            coding:
              - { system: http://icu.emr.local/CodeSystem/his-route, code: '913' }
          dose: { value: 300, unit: mg, system: http://unitsofmeasure.org, code: mg }

    ObservationBloodPressure:
      summary: 血壓（單一 Observation ＋ 兩個 component）
      value:
        resourceType: Observation
        status: final
        category:
          - coding:
              - { system: http://terminology.hl7.org/CodeSystem/observation-category, code: vital-signs }
        code:
          coding:
            - { system: http://loinc.org, code: 85354-9, display: Blood pressure panel }
          text: 血壓
        subject: { reference: Patient/demo-a }
        effectiveDateTime: '2026-08-31T18:00:00+07:00'
        component:
          - code:
              coding:
                - { system: http://loinc.org, code: 8480-6 }
                - { system: http://icu.emr.local/CodeSystem/tdl, code: TDL.0094, display: 收縮壓 }
            valueQuantity: { value: 125, unit: mmHg, system: http://unitsofmeasure.org, code: 'mm[Hg]' }
          - code:
              coding:
                - { system: http://loinc.org, code: 8462-4 }
                - { system: http://icu.emr.local/CodeSystem/tdl, code: TDL.0095, display: 舒張壓 }
            valueQuantity: { value: 77, unit: mmHg, system: http://unitsofmeasure.org, code: 'mm[Hg]' }

    ProvenanceExecutionReview:
      summary: 醫師核對護理執行（差異待追蹤）
      value:
        resourceType: Provenance
        extension:
          - url: http://icu.emr.local/StructureDefinition/reviewOutcome
            valueCode: difference
          - url: http://icu.emr.local/StructureDefinition/reviewNote
            valueString: 實際給藥時間較預定晚 45 分鐘，已與護理端確認原因並列入追蹤。
        target:
          - reference: MedicationAdministration/ma-m3-d1
        recorded: '2026-08-31T10:15:03+07:00'
        activity:
          coding:
            - system: http://icu.emr.local/CodeSystem/provenance-activity
              code: clinical-review
              display: 臨床核對／判讀
        agent:
          - type:
              coding:
                - { system: http://terminology.hl7.org/CodeSystem/provenance-participant-type, code: verifier }
            who: { reference: PractitionerRole/doctor-d1 }

    FiveRightsIncomplete:
      summary: 五對核對未完成
      value:
        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').extension('rightDose')
