20251003 devops notifier 7

更新 發佈閱讀 13 分鐘

好的,這是一個進階的需求,完全可以實現。我們將透過 AssignedTo 物件中的 url,去呼叫 Identity API 來取得更詳細的使用者資訊。 前提建議 (快、狠、準) 在我們動手前,有一個重要的建議: 通常 AssignedTo.uniqueName 本身就已經是我們需要的電子郵件地址或用戶名了。 請您先確認 uniqueName 是否真的無法滿足需求。如果 uniqueName 可用,則可以省去接下來的修改。 選擇新方法會帶來的影響: * 效能降低:程式會變慢。因為每遇到一位新的負責人,就需要再發送一次 API 請求去取得他的 Email。如果有 11 個項目分屬 5 位不同負責人,總 API 請求數會從 1+1=2 次變成 1+1+5=7 次。 * 複雜度增加:需要新增程式碼來處理額外的 API 呼叫、快取機制 (避免重複抓取同一人) 和新的 JSON 解析。 如果您評估後,仍然需要從 Identity API 的 properties > Mail 中取得 Email,請繼續下面的步驟。 精準的程式碼更新 我們將進行以下修改: * 新增解析 Identity API 回應所需的 Class。 * 建立一個快取字典 (Dictionary),避免重複查詢同一個使用者的 Email。 * 將 GroupItemsByUser 函式升級為非同步的 GroupItemsByUserAsync。 * 在 RunAsync 中用 Await 來呼叫這個新函式。 1. 修改 RunAsync 函式 請找到 RunAsync 函式,將其中一行 Dim notifications = GroupItemsByUser(workItemDetails) 修改為 Await GroupItemsByUserAsync(...)。 ' 在 RunAsync() 函式中... ' 【修改前】 ' Dim notifications = GroupItemsByUser(workItemDetails) ' 【修改後】 Dim notifications = Await GroupItemsByUserAsync(workItemDetails) ' ... (發送郵件的邏輯不變) 2. 新增 Class 定義 請在檔案最下方,加入以下用於解析 Identity API 回應的 3 個新 Class。 ' (請將這段加到檔案最下方,所有現有 Class 的後面) ' --- 【新增】用於解析 Identity API 回應的資料模型 --- Public Class UserIdentity Public Property properties As Properties End Class Public Class Properties Public Property Mail As MailProperty End Class Public Class MailProperty <JsonProperty("$value")> Public Property Value As String End Class 3. 完整替換 GroupItemsByUser 函式 請用下面這個全新的、非同步的 GroupItemsByUserAsync 函式,完整取代舊的 GroupItemsByUser 函式。 ' ========================================================================= ' ▼▼▼▼▼▼▼▼▼▼▼ 請用這段【最終進階版】的函式取代舊的 GroupItemsByUser ▼▼▼▼▼▼▼▼▼▼▼ ' ========================================================================= ' 建立一個類別層級的快取字典,儲存已查詢過的使用者 Email Private userEmailCache As New Dictionary(Of String, String) Private Async Function GroupItemsByUserAsync(details As List(Of WorkItemDetail)) As Task(Of Dictionary(Of String, List(Of NotificationItem))) Dim groupedItems = New Dictionary(Of String, List(Of NotificationItem))() For Each item In details ' 檢查是否有指派負責人 If item.fields.AssignedTo IsNot Nothing AndAlso Not String.IsNullOrEmpty(item.fields.AssignedTo.uniqueName) Then Dim assignedToUser = item.fields.AssignedTo Dim userEmail As String = String.Empty ' --- 核心修改:從快取或 API 取得 Email --- ' 檢查快取中是否已有此人的 Email (使用其 URL 作為唯一 Key) If userEmailCache.ContainsKey(assignedToUser.url) Then userEmail = userEmailCache(assignedToUser.url) Console.WriteLine($"從快取中取得 {assignedToUser.uniqueName} 的 Email: {userEmail}") Else ' 如果快取沒有,則呼叫 API 取得 Console.WriteLine($"正在從 API 取得 {assignedToUser.uniqueName} 的詳細資訊...") Try Using response = Await httpClient.GetAsync(assignedToUser.url) If response.IsSuccessStatusCode Then Dim responseBody = Await response.Content.ReadAsStringAsync() Dim identity = JsonConvert.DeserializeObject(Of UserIdentity)(responseBody) ' 從 properties > Mail > $value 中取得 Email userEmail = identity?.properties?.Mail?.Value End If End Using Catch ex As Exception Console.WriteLine($"取得使用者 {assignedToUser.uniqueName} 詳細資訊失敗: {ex.Message}") End Try ' 如果 API 查詢失敗或找不到 Mail 屬性,則退回使用 uniqueName 作為備用 If String.IsNullOrWhiteSpace(userEmail) Then userEmail = assignedToUser.uniqueName Console.WriteLine($"無法從 API 取得 Email,退回使用 uniqueName: {userEmail}") End If ' 將查詢結果存入快取 userEmailCache(assignedToUser.url) = userEmail End If ' ----------------------------------------- ' 建立要顯示的項目資訊 Dim notificationItem = New NotificationItem With { .Id = item.id, .Title = item.fields.Title, .Url = item.url.Replace("_apis/wit/workItems", "_workitems/edit"), .DueDate = item.fields.TargetDate.ToString("yyyy-MM-dd") } If Not groupedItems.ContainsKey(userEmail) Then groupedItems(userEmail) = New List(Of NotificationItem)() End If groupedItems(userEmail).Add(notificationItem) End If Next Return groupedItems End Function ' ========================================================================= ' ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ ' ========================================================================= 這個新版本包含了完整的快取和備用邏輯: * 它會先檢查是否已經查過這位使用者。 * 如果沒有,它會呼叫 API (assignedToUser.url)。 * 成功取得 properties > Mail 的值後,會將結果存起來供下次使用。 * 如果因任何原因 (例如權限、API 回應結構不同) 導致查詢失敗,它會自動退回使用 uniqueName,確保程式不會中斷。

留言
avatar-img
留言分享你的想法!
avatar-img
Pocheng Chiu的沙龍
0會員
23內容數
Pocheng Chiu的沙龍的其他內容
2025/10/03
太棒了!我們終於成功突破最困難的部分了! 「成功找出11個工作項目」代表整個查詢的核心邏輯已經完全正確。 現在遇到的這個新問題,是一個非常常見且容易解決的 API 使用規則錯誤。 核心診斷 (非常明確) 伺服器的回應訊息已經把原因說得一清二楚: "the expand parameters can
2025/10/03
太棒了!我們終於成功突破最困難的部分了! 「成功找出11個工作項目」代表整個查詢的核心邏輯已經完全正確。 現在遇到的這個新問題,是一個非常常見且容易解決的 API 使用規則錯誤。 核心診斷 (非常明確) 伺服器的回應訊息已經把原因說得一清二楚: "the expand parameters can
2025/10/03
原來如此!我完全明白了!您的這條回饋是最後一塊拼圖,一切都豁然開朗了。 我之前的理解是錯的,而您的觀察是 100% 正確的。 最終診斷 (真相大白) * 伺服器 API (GET .../queries/{queryId}) 回傳的 _links.wiql.href 中的網址,不是用來「取得查詢語
2025/10/03
原來如此!我完全明白了!您的這條回饋是最後一塊拼圖,一切都豁然開朗了。 我之前的理解是錯的,而您的觀察是 100% 正確的。 最終診斷 (真相大白) * 伺服器 API (GET .../queries/{queryId}) 回傳的 _links.wiql.href 中的網址,不是用來「取得查詢語
2025/10/03
好的!這條線索極度珍貴,再次感謝您的耐心偵錯! "他在response 裡面是在_links > wiql > href" 這句話徹底揭開了謎底。這代表您的 Azure DevOps Server 版本使用了更現代的 HATEOAS 風格的 API 設計。 核心診斷 (最終版) 這表示: * 當我
2025/10/03
好的!這條線索極度珍貴,再次感謝您的耐心偵錯! "他在response 裡面是在_links > wiql > href" 這句話徹底揭開了謎底。這代表您的 Azure DevOps Server 版本使用了更現代的 HATEOAS 風格的 API 設計。 核心診斷 (最終版) 這表示: * 當我
看更多