---
title: "套件結構統一：給 PM 的說明"
brand: "Guidant AI · **FR-089／FR-091** 零件規格統一"
eyebrow: "給 PM 的說明 · 不需要技術背景"
h1: "零件是怎麼裝進系統的"
lede: "Guidant AI 由一個主體加 21 個零件組成。這頁講清楚兩件事：一個零件實際上是怎麼裝進系統的、這套裝法對交付有什麼意義。全篇不用技術名詞，看完應該能跟工程師討論這個題目。"
chips: [
  {text: "21 個零件裝法一致", kind: ok},
  {text: "拔掉任一顆不影響其他", kind: ok},
  {text: "零件已出版、主體待進版", kind: warn},
  {text: "工程師版另見插件解剖", kind: plain}
]
footer: "同一個站的「插件解剖」是本頁的工程師版，內容相同但用技術語言、附程式碼。本頁只描述現況，做過哪些工作與沿革見 FR-089 工作日誌與 git log。本頁由 CM-1736 產出。"
---

## 系統長什麼樣 {#shape nav="系統長什麼樣"}

Guidant AI 不是一整塊做出來的。它是**一個主體**加上**21 個零件**：

::: grid2
::: {.card .ok}
#### 主體

管全系統共通的事：誰登入了、這個人有沒有權限、資料庫連線、畫面的外框、系統設定。它認識所有零件。
:::
::: {.card}
#### 21 個零件

一個零件就是一塊完整的功能——資產管理、公告、問卷、檢測工具、權限、通知、檔案上傳……每一塊都可以獨立拆下來。**零件之間互不認識**。
:::
:::

「零件之間互不認識」是刻意的規矩：問卷不可以直接去翻資產管理的資料。要跨零件拿東西，一律經過主體。這樣任何一個零件壞了、拔了，其他零件不會跟著出事。

## 一個零件是怎麼裝上去的 {#how nav="怎麼接的"}

這是本頁的重點。裝一個零件，主體要做**三件事**——21 個零件都是這三件，不多不少。

::: {.callout .decided}
**三件事**

**① 告訴系統「我要用這顆零件」**——在一份清單上寫下零件的名字與版本，系統下次啟動就會把它抓進來。等於採購單上加一筆。

**② 寫一張「交接表」**——這是三件裡唯一要動腦的。零件被設計成「我不自己決定共通的事」，所以它會列出一張表，上面是**它需要主體提供什麼**，主體逐格填答。詳見下一節。

**③ 在安裝名單上加一列**——系統啟動時照這份名單一個一個裝。加一列就會裝，拿掉一列就不裝了。
:::

第 ③ 件有個副作用很重要：**把那一列拿掉、重新啟動，那個零件的功能就整個消失，其他零件完全不受影響。** 這是刻意設計的，也實際驗過——把檢測工具那顆拿掉後，屬於它的 35 個網址如預期全部消失，其餘頁面正常運作。

```{.mermaid cap="圖 1 — 裝一個零件要做的三件事"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
  S1["① 採購單加一筆<br/>寫下零件名字與版本"]
  S2["② 寫一張交接表<br/>零件列出它要什麼<br/>主體逐格填答"]
  S3["③ 安裝名單加一列<br/>系統啟動時照名單裝"]
  GO["系統啟動<br/>零件的功能就出現了"]
  S1 --> S2 --> S3 --> GO
```

## 交接表：零件要什麼，主體填什麼 {#handover nav="交接表"}

零件被設計成**不自己處理共通的事**。它不自己判斷「這個人有沒有登入」，也不自己決定「這個人能不能按這個按鈕」——這些由主體統一管，否則 21 個零件各判各的，規則遲早不一致。

所以每個零件都會列一張表，上面寫著「請主體給我這些」。以資產管理為例，表上有八格，大致是三類：

| 零件要什麼 | 主體填什麼 | 為什麼不能零件自己來 |
|---|---|---|
| 一個「驗這個人登入了沒」的方法 | 主體現行的登入驗證 | 全系統要用同一套登入規則 |
| 一個「驗這個人有沒有這項權限」的方法 | 主體的權限判定 | 同上，而且權限規則會改，改一處就好 |
| 「這台設備被幾個稽核任務用到」的答案 | 主體去問任務那顆零件，再回答 | **答案在別的零件手上，資產零件不准直接去問** |
| 「這個人的暱稱是什麼」的答案 | 主體去問權限那顆零件，再回答 | 同上 |

最後兩列是關鍵：跨零件的事**一律由主體轉手**。資產零件只說「我需要一個能回答這件事的東西」，至於答案怎麼來的，它不知道也不需要知道。這就是為什麼拔掉任何一顆都不會連累別人——它們從來沒有直接勾在一起。

## 什麼時候交、什麼時候用 {#timing nav="交與用"}

交接表填好之後，東西是**在系統啟動時一次交給零件**的，不是每次有人操作才交。零件把主體交來的東西收在一個固定的地方；之後每次有人操作（點一個按鈕、開一個頁面），零件就到那個地方把需要的東西拿出來用。

::: {.callout}
**為什麼要分兩個時間**

零件的程式在「被載入」的當下就定型了，而那個時間點比「主體把東西交過來」還要早——零件根本還沒拿到東西，不可能當場寫死。

用租店面來想：裝潢的時候（零件被載入）先把**收銀機的位置**留好，但收銀機本身（主體的登入驗證）要等房客搬進來（系統啟動、主體交接）才放上去。每次客人結帳（有人操作），店員走到那個位置拿收銀機來用。

這個設計的好處是：主體哪天換了一套登入方式，21 個零件一個都不用改——它們拿的一直是「那個位置上的東西」，不是某個寫死的特定作法。
:::

```{.mermaid cap="圖 2 — 啟動時交一次，之後每次操作去拿"}
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB','actorBkg':'#E2F0F1','actorTextColor':'#14201F','actorBorder':'#0E7C86','actorLineColor':'#C7D1D2','signalColor':'#4A5A5C','signalTextColor':'#14201F','labelBoxBkgColor':'#EEF2F3','labelBoxBorderColor':'#C7D1D2','labelTextColor':'#14201F','loopTextColor':'#14201F','noteBkgColor':'#F6EBD5','noteTextColor':'#14201F','noteBorderColor':'#9C6B12','activationBkgColor':'#EEF2F3','activationBorderColor':'#C7D1D2','sequenceNumberColor':'#FFFFFF'}}}%%
flowchart LR
  subgraph boot["系統啟動時（只做一次）"]
    direction LR
    H["主體<br/>把填好的交接表交出去"]
    P["零件<br/>① 把自己的頁面掛上系統<br/>② 把主體給的東西收好"]
    H --> P
  end
  subgraph use["之後每次有人操作"]
    direction LR
    R["使用者點了「修改設備」"]
    Q["零件去收東西的地方<br/>拿出「驗登入」「驗權限」<br/>驗過了才做事"]
    R --> Q
  end
  BOX[("零件收東西的地方")]
  P -- 放進去 --> BOX
  Q -. 拿出來 .-> BOX
```

## 21 個零件一視同仁 {#uniform nav="一視同仁"}

前面描述的裝法，**21 個零件完全相同**——沒有哪一顆比較特別、需要額外招數。具體是這四件事一致：

| | 規定 |
|---|---|
| 主體要動幾個地方 | 一律三個 |
| 交接表擺在哪 | 一個檔案看完 |
| 「去拿東西」那個動作叫什麼 | 21 個零件同一個名字 |
| 零件內部檔案怎麼分類 | 同一套分類 |

這件事的價值在人：新人搞懂一顆零件要花約半天，**而學會一顆就等於學會二十一顆**——經驗可以直接換到下一顆用。

::: {.callout .decided}
**這套規矩不影響任何功能**

它管的是「零件怎麼裝」，不是「零件做什麼」。使用者看得到的畫面、操作流程、資料內容，以及權限怎麼判斷的規則，都不受它左右。
:::

### 權限開關由零件自己帶著

權限開關就是「誰能按哪個按鈕」的那些項目，全系統目前 80 個。**每個零件自己列出「我需要哪些開關」，系統有一道自動對帳**在核對零件的清單與資料庫裡的清單。

這道對帳擋的是一種不會有錯誤訊息的錯：零件加了新開關卻沒登記進資料庫，客戶新裝的機器上那個按鈕就永遠按不下去，通常要等客戶抱怨才發現。有了對帳，漏了當場測試變紅，不會流到客戶手上。

::: statgrid
::: stat
[21]{.v}[個零件全部統一]{.k}
:::
::: {.stat .ok}
[3]{.v}[個地方就裝得起來]{.k}
:::
::: stat
[80]{.v}[個權限開關納入自動對帳]{.k}
:::
:::

## 對交付的意義，和還沒做的事 {#impact nav="意義"}

意義有三點：**交接成本低**（接手一顆零件不必從頭摸索）、**一類無聲的錯有自動守門**（權限漏登記當場擋下）、**加新零件有固定作法**（不必每次重新發明）。這些不會出現在功能清單上，但會反映在每一次加功能的速度和出錯率上。

目前走到哪、還沒做的，誠實列出來：

::: {.callout .ok}
**零件已出版，21 個都有正式版號**

21 個零件已全部出版到公司內部的套件倉庫（20 個是 1.0.0，OSCAL 資料模型那個沿用既有的 2.x 系列進到 2.3.0），主體也改成指向這些正式版本，開發機上已用出版後的零件跑起來驗過。
:::

::: {.callout .warn}
**一、主體還沒進版、還沒上測試機與正式環境**

零件出版了，但主體本身的版本號還沒進、發布說明還沒寫、也還沒部署到測試機。要讓客戶端用到，還要走一次主體的正式出版流程，時間由決策者定。
:::

::: {.callout .warn}
**二、權限開關「自動寫進客戶資料庫」還沒做**

目前做到的是「每個零件有清單、系統有對帳」。理想狀態是零件裝上去時，需要的開關自動寫進客戶的資料庫；那牽涉到既有客戶資料的處理，是下一個獨立的大工程。
:::
