商業視角說明

Go-Live:Google Sheet to Jira 工單自動化

從雜亂的回報到結構化的 Jira 工單,實現零人工、高品質、即時通知的自動化閉環。

The Challenge

「系統上線期間問題回報量大,人工將 Google Sheet 回報轉錄為 Jira 工單不僅耗時(每張 3-5 分鐘),且常因格式不一導致溝通成本增加。」

The Solution

「開發一個 n8n 自動化工作流,定時讀取 Google Sheet,自動將回報轉換為格式標準化的 Jira 工單,並透過 Webhook 即時發送 Teams 通知,實現了從回報到指派的零人工介入閉環。」

專案緣起:Go-Live 期間的大作戰

系統上線 (Go-Live) 初期,是資訊團隊壓力最大的時刻。問題回報會從四面八方湧入:電話、LINE 群組、甚至是走廊上的攔截。為了快速記錄,大家習慣先將問題登記在一個共用的 Google Sheet,等釐清問題後,再手動建立 Jira Ticket。

但這個過程也衍生了一些後遺症:

  • 大量人工: 開票時的複製貼上、欄位對應,開一張平均耗時 3-5 分鐘,一天下來就是數小時的人工作業。
  • 品質不一: 不同人開的票,格式、資訊完整度參差不齊,PM 跟 RD 可能需要來回溝通才能釐清問題。
  • 資訊延遲: 手動轉錄造成延遲,緊急問題可能無法在第一時間被看見,更麻煩的是,開完票還得手動貼連結到 Teams 通知大家。

身為一個懶惰卻追求效率的部分工時 QA 工程師,我覺得這樣其實不可以。因此,在專案上線的緊繃時刻,我擠出一些業餘時間,用 n8n 打造了這個自動化工作流,旨在將團隊從繁瑣的人工作業中解放出來。

🐦重點重點重點是我把這個工作流,跟之前做的 LineBot 工作流結合成 Combo 技:

第一線同仁用 Line 回報並自動寫到 Google Sheets,然後再由這個工作流來自動 Create Jira Ticket,完美。 (查看 LineBot 回報機器人介紹 )


理念核心:一個聰明且自律的自動化閉環

這個 n8n 工作流的核心思想是「監控、處理、回饋、通知」,形成一個完整的自動化閉環。

  1. 定時監控 (Schedule Trigger): 工作流每 15 分鐘自動啟動一次,像個忠誠的哨兵,檢查 Google Sheet 中是否有新的任務。
  2. 智慧篩選 (Google Sheets Node): 它只抓取滿足兩個條件的資料列:Jira Link 欄位為空,且 可Jira 欄位被標記為 "Y"。這確保了只有經過核對確認、且尚未開票的回報才會進入流程。
  3. 格式化處理 (Code Node): 這是確保工單品質的關鍵。此節點會讀取所有相關欄位,將其組合成一個結構清晰、重點分明、甚至帶有彩色區塊的 Jira Wiki Markup 描述。同時,它會根據回報的「嚴重度等級」自動對應成 Jira 的 Priority ID。
  4. 自動開票 (Jira Node): 將格式化後的內容,連同 Assignee、Reporter、Labels 等資訊,自動建立一張高品質的 Jira Bug Ticket。
  5. 即時回饋 (Google Sheets Node): Jira Ticket 建立成功後,工作流會立即將含有連結的 Jira Key 回寫到 Google Sheet 的 Jira Link 欄位,並將 可Jira 狀態更新為 "Done",形成完美的資訊閉環。
  6. 主動通知 (HTTP Request to Power Automate): 最後,工作流會組合一個精美的 Teams Adaptive Card,透過呼叫 Power Automate 的 Webhook,將新工單的關鍵資訊即時推送到團隊頻道,確保重要問題不會被遺漏。
  7. 錯誤告警 (Telegram Node): 在流程的關鍵節點(如讀取資料、建立工單)都設置了錯誤處理,一旦發生問題,會立即發送通知到我的 Telegram,讓我能即時介入。

工作流展示

點擊下方頁籤,查看此工作流的實際運行示意:

n8n 整體工作流

從定時觸發、讀取 Google Sheet、格式化、建立 Jira Ticket、回寫狀態,到最後發送 Teams 通知,所有步驟一目了然。

n8n 整體工作流
輸入源:Google Sheet

維運團隊只需專注於填寫這張表單,並在確認問題後,於「可Jira」欄位填上 "Y",剩下的就交給自動化。

Google Sheet 輸入
產出成果:結構化的 Jira Ticket

自動化流程確保每一張 Ticket 都擁有標準、清晰、帶有彩色區塊的描述,大幅提升 RD 的處理效率。

Jira 產出
主動通知:Microsoft Teams 卡片

工單建立後,關鍵資訊會被整理成一張精美的卡片,即時發送到團隊頻道,讓所有人同步最新動態。

Teams 通知

從 0 到 1:建立你的 Go-Live 自動化工作流

本教學將引導你完成這個 n8n 工作流的每一步。在開始之前,請確保你已經擁有 n8n 環境,並準備好 Google、Jira 和 (可選的) Telegram / Power Automate 的 API 權限。

這是工作流的起點,決定了 n8n 檢查新任務的頻率。

  1. 在 n8n 畫布中,新增一個 Schedule Trigger 節點。
  2. Trigger Interval 設定為你需要的頻率,例如每 15 分鐘 (Minutes)

這個節點會定時啟動,並將一個空的執行訊號傳遞給下一個節點。

此節點負責從 Google Sheet 中讀取需要被建立為 Jira Ticket 的回報。

  1. 新增一個 Google Sheets 節點。
  2. Authentication 中選擇或建立你的 Google API 憑證。
  3. Resource 選擇 RowOperation 選擇 Get
  4. 填入你的 Spreadsheet IDSheet Name
  5. Filters 區塊設定篩選條件,這是確保不重複開票的關鍵:
    • Filter 1: Jira Link | Condition: Is Empty | Value: (留空)
    • Filter 2: 可Jira | Condition: Equals | Value: Y
    • Filter 3: 問題描述 | Condition: Not Equals | Value: (留空)
  6. (重要) 在節點設定的 Settings 頁籤中,將 On Error 設定為 "Continue (using error output)"。這會產生一個額外的 "error" 輸出錨點,讓我們可以連接到錯誤通知節點。

如果 Google Sheet 節點因為權限、網路問題等原因執行失敗,我們希望立即收到通知。

  1. 新增一個 Telegram 節點。
  2. 設定你的 Telegram API CredentialsChat ID
  3. Text 欄位中,填寫一個清晰的錯誤訊息,例如:「❌ GoLive Jira 自動化 - 讀取 Google Sheet 失敗!」。
  4. 將上一個 Google Sheets 節點的 error output (紅色圓點) 連接到此 Telegram 節點的 input。

Google Sheet 節點可能一次返回多個需要處理的列。為了確保每個回報都被獨立、穩定地處理,我們需要一個迴圈。Split in Batches 節點可以完美勝任此工作。

  1. 新增一個 Split in Batches 節點。
  2. Batch Size 設定為 1

這樣設定後,該節點會將收到的多個項目(Items)拆分成獨立的執行,一次只處理一個,直到所有項目都處理完畢。

這是提升工單品質的核心。此節點會用 JavaScript 將 Google Sheet 的原始資料轉換成結構化的 Jira Wiki Markup,並對應優先級。

  1. 新增一個 Code 節點。
  2. 將以下 JavaScript 程式碼貼入程式碼編輯區。

/**
 * n8n Code Node: Format Issue Report for Jira Description (Wiki Markup) & Map Priority
 * 
 * This script takes raw issue data and performs two tasks:
 * 1. Transforms it into a structured Wiki Markup string for Jira descriptions.
 * 2. Maps the text-based priority level ('嚴重度等級') to a numeric Jira Priority ID.
 *    If the priority level is not found or is empty, it defaults to a standard ID.
 */

// --- 1. Get data from the upstream node ---
const itemJson = $input.item.json;

// --- 2. Priority Mapping ---
const priorityMap = {
  'P1': '1', // Highest
  'P2': '2', // High
  'P3': '3', // Medium
  'P4': '4', // Low
  // Default/fallback value if no match is found
  '_': '3'  // Default to Medium
};

// --- 3. Helper function to create a wiki markup list item ---
function createListItem(emoji, label, value, defaultValue = 'N/A') {
  let displayValue = value;
  if (typeof value === 'boolean') {
    displayValue = value ? '是' : '否';
  }
  // Ensure the value is not null or undefined before displaying
  return `* ${emoji} *${label}:* ${displayValue || defaultValue}`;
}

// --- 4. Build the Wiki Markup String ---

// Panel 1: Issue Report Information
const reportInfoPanel = [
  '{panel:title=問題回報資訊|bgColor=#DEEBFF|borderColor=#B3D4FF}',
  createListItem('💻', '系統別/地點', itemJson['系統別/地點']),  
  createListItem('📝', '問題簡述', itemJson['問題簡述']),
  createListItem('👤', '回報者姓名', itemJson['提出者']),  
  createListItem('🚨', '緊急', itemJson['緊急']),  
  createListItem('👨‍⚕️', '職務', itemJson['職務']),
  createListItem('📞', '聯絡方式', itemJson['聯絡方式']),
  createListItem('🗂️', '病歷號/住院號', itemJson['病歷號/住院號']),
  createListItem('📍', '診間或病房編號', itemJson['診間或病房編號']),
  '{panel}'
].join('\n');

// Panel 2: Internal Processing Status
const internalStatusPanel = [
  '{panel:title=內部處理狀態|bgColor=#FEF0B5|borderColor=#FFC400}',
  createListItem('🚦', '嚴重度等級', itemJson['嚴重度等級'], '未分類'),
  createListItem('🧑‍💻', 'Owner', itemJson['Owner(RD)'], '未指派'),
  createListItem('⏳', '處理狀態', itemJson['處理狀態'], '待處理'),
  createListItem('🛠️', '處理方式', itemJson['處理方式'], '分析中'),
  '{panel}'
].join('\n');

// --- 5. Combine all parts and map priority ---
// Combine panels into a single description string
itemJson.jiraDescriptionString = `${reportInfoPanel}\n\n${internalStatusPanel}`;

// Map the priority using the text from the sheet, or use the default
itemJson.priorityId = priorityMap[itemJson['嚴重度等級']] || priorityMap['_'];

// --- 6. Return the enhanced item ---
return itemJson;
                                        

使用上一步格式化好的內容,正式在 Jira 中建立一張 Bug 單。

  1. 新增一個 Jira 節點,設定好 API 憑證。
  2. Resource 選擇 IssueOperation 選擇 Create
  3. 選擇你的 ProjectIssue Type (例如:Bug)。
  4. Summary 欄位,使用表達式填入標題,例如:=[Hospital] Prod - {{ $json['問題簡述'] }}
  5. 點擊 Add Field,選擇 Description。在 Description 欄位中,使用表達式填入上一步產生的 Wiki Markup 字串:={{ $json.jiraDescriptionString }}
  6. 同樣地,點擊 Add Field -> Priority。在 Priority 欄位右側點擊「...」選擇 Add Expression,然後填入 ={{ $json.priorityId }}
  7. 你可以用同樣的方式設定 Assignee (負責人)Reporter (回報人)Labels (標籤) 等欄位。
  8. 同樣地,為此節點設定 On Error"Continue (using error output)",並連接到另一個 Telegram 錯誤通知節點。

建立成功後,必須更新 Google Sheet 的狀態,以形成閉環,避免重複處理。

  1. 新增另一個 Google Sheets 節點。
  2. Operation 選擇 Update
  3. 填寫與步驟 2 相同的 Spreadsheet IDSheet Name
  4. Key Column 中,填入你的 Google Sheet 中用來唯一識別每一列的欄位名稱,例如 row_number
  5. Fields to Update 區塊:
    • Key: Jira Link | Value (使用表達式): =https://YOUR_JIRA_DOMAIN/browse/{{ $('Create an issue').item.json.key }}
    • Key: 可Jira | Value: Done

最後,我們將新建立的工單資訊,以易於閱讀的卡片形式發送到 Teams 頻道。

  1. (Power Automate) 建立一個 Power Automate 流程,觸發器選擇 "When an HTTP request is received",儲存後會產生一個 Webhook URL。
  2. (n8n) 新增一個 Code 節點,用來產生 Teams Adaptive Card 的 JSON 結構。這需要你熟悉 Adaptive Card 的語法,或使用 Adaptive Card Designer 來設計。
  3. (n8n) 新增一個 HTTP Request 節點。
  4. Method 設為 POST
  5. URL 欄位貼上你在 Power Automate 取得的 Webhook URL。
  6. Body Content Type 選擇 JSON
  7. Body 欄位,使用表達式填入上一步 Code 節點產生的 Adaptive Card JSON 變數。

當工作流執行到此處時,就會呼叫 Power Automate,進而將一張包含工單摘要、負責人、嚴重性等關鍵資訊的卡片發送到指定的 Teams 頻道。

成果與反思:將時間還給「人」

這個專案的價值,遠不止是省下幾分鐘的複製貼上。

  • 時間價值的最大化 將團隊從重複性、低價值的行政工作中解放,讓團隊成員能專注於問題分析與用戶溝通。
  • 品質與一致性 自動化確保了每張工單的品質與格式一致,降低了溝通成本,也讓問題追蹤與數據分析變得更加可靠。
  • 主動的團隊協作 從「被動查詢」到「主動通知」,Teams 的即時通知機制,讓團隊的反應速度與協作效率提升了一個檔次。

💬 想要討論或分享你的想法?

歡迎到我的 LinkedIn 文章留言,與我和其他讀者一起交流討論! 無論是對這個案例的看法、你的實作經驗,或是任何問題,都歡迎分享。

前往 LinkedIn 討論