29.【.NET8 實戰--孢子記賬--從單體到微服務--轉向微服務】--單體轉微服務--用戶配置服務

用戶配置服務是孢子記賬中最簡單的部分。簡單說,用戶配置服務就是用戶自定義的配置項存儲服務,用于我們的APP根據用戶的配置實現指定的功能。它提供了一個簡單的接口,允許用戶存儲和檢索他們的配置數據。就目前來說,用戶配置只有一個配置項:默認幣種設置。在后續的版本中,我們會根據用戶的反饋和需求,添加更多的配置項。

Tip:應大多數讀者要求,在這篇文章開始,我們將只講解每個服務的核心內容。完整代碼請訪問課程GitHub倉庫代碼。

一、用戶配置服務實現

用戶配置服務我們只需要對外開放兩個Web Api :獲取所有配置、更新配置,我們一起來實現這兩個接口。

1.1 獲取所有配置

獲取所有配置,用于獲取當前用戶的所有配置項。我們可以通過一個簡單的GET請求來實現這個功能。首先,我們需要在IConfigServer接口中定義一個方法來獲取所有配置項。然后,我們需要在ConfigController中實現這個方法.

IConfigServer接口中添加方法定義:

/// <summary>
/// 查詢用戶配置
/// </summary>
/// <returns>用戶配置</returns>
List<ConfigResponse> GetConfig();

接口方法很簡單,沒必要多講,我們重點看一下ConfigController的實現。實現代碼如下:

/// <summary>
/// 查詢用戶配置
/// </summary>
/// <returns>用戶配置</returns>
public List<ConfigResponse> GetConfig()
{long userId = _contextSession.UserId;// 查詢用戶配置List<Config> configs = _context.Configs.Where(c => c.UserId == userId).ToList();// 將配置實體轉換為響應模型List<ConfigResponse> configResponses = _autoMapper.Map<List<ConfigResponse>>(configs);return configResponses;
}

在這個方法中,我們首先獲取當前用戶的ID,然后查詢數據庫中與該用戶相關的所有配置項。最后,我們將查詢結果轉換為響應模型并返回。
在實際應用中,我們可能需要對配置項進行分頁查詢,以提高性能和用戶體驗。這里我們暫時不做分頁處理,是因為當前用戶配置項不多,待配置型變多后我們會在后續版本會添加。

Tip:代碼中的_contextSession是當前用戶的會話上下文,里面存儲了當前用戶的ID和用戶名,它方便我們在服務中獲取當前用戶的信息。具體實現我們將在后續的用戶會話服務中講解。在這里我們只需要知道它可以幫助我們獲取當前用戶的ID即可。

在Controller中我們實現調用GetConfig方法的邏輯:

/// <summary>
/// 獲取所有配置
/// </summary>
/// <returns>用戶配置列表</returns>
[HttpGet]
public ActionResult<List<ConfigResponse>> GetConfigs()
{List<ConfigResponse> configs = _configServer.GetConfig();return Ok(configs);
}

在這個方法中,我們調用了_configServer.GetConfig()來獲取所有配置項,并將結果返回給客戶端。

1.2 更新配置

更新配置,用于更新當前用戶的配置項。我們可以通過一個簡單的POST請求來實現這個功能。首先,在IConfigServer接口中定義一個方法來更新配置項。

/// <summary>
/// 更新用戶配置
/// </summary>
/// <param name="config">配置更新請求</param>
void UpdateConfig(ConfigResponse config);

接著,在ConfigController中實現這個方法。實現代碼如下:

/// <summary>
/// 更新用戶配置
/// </summary>
/// <param name="config">配置更新請求</param>
public void UpdateConfig(ConfigResponse config)
{long userId = _contextSession.UserId;// 查詢用戶配置Config? existingConfig = _context.Configs.FirstOrDefault(c => c.Id == config.Id);if (existingConfig == null){throw new NotFoundException("配置項不存在");}existingConfig.Value = config.Value;SettingCommProperty.Edit(existingConfig);_context.Configs.Update(existingConfig);// 保存更改到數據庫_context.SaveChanges();
}

在這個方法中,我們首先獲取當前用戶的ID,然后查詢數據庫中與該配置項相關的配置項。如果配置項不存在,則拋出一個NotFoundException異常。接著,我們更新配置項的值,并保存更改到數據庫。
在Controller中我們實現調用UpdateConfig方法的邏輯:

/// <summary>
/// 更新配置
/// </summary>
/// <param name="config">配置更新請求</param>
/// <returns>更新結果</returns>
[HttpPut]
public ActionResult<bool> UpdateConfig([FromBody] ConfigResponse config)
{_configServer.UpdateConfig(config);return Ok(true);
}

在這個方法中,我們調用了_configServer.UpdateConfig(config)來更新配置項,并將結果返回給客戶端。

二、接收用戶注冊后的配置設置

在這一小節,我們將實現一個功能:當用戶注冊成功后,自動為用戶創建默認的配置項。這樣,用戶在第一次使用應用時就可以直接使用默認配置,而不需要手動設置。
要實現這個功能有兩種方式:一種是用戶注冊服務中調用配置服務,另一種是直接在通過事件機制。我們先來對比一下這兩種方式。

  • 用戶注冊服務調用配置服務:在用戶注冊服務中,我們可以在用戶注冊成功后,直接調用配置服務來創建默認配置項。這樣做的好處是簡單直接,不需要額外的事件機制支持。但是,這種方式會導致用戶注冊服務和配置服務之間的耦合度較高,增加了系統的復雜性。
  • 通過事件機制:我們可以在用戶注冊成功后,發布一個事件,然后在配置服務中訂閱這個事件。當事件被觸發時,配置服務會自動創建默認配置項。這種方式的好處是解耦了用戶注冊服務和配置服務,使得系統更加靈活和可擴展,如果配置服務需要修改或替換,只需要修改配置服務的實現,而不需要修改用戶注冊服務的代碼。

我們在這里選擇第二種方式,通過事件機制來實現用戶注冊后的配置設置,這樣可以使得系統更加靈活和可擴展,實現事件機制的方法我們選擇使用RabbitMQ消息隊列來實現。
首先,我們需要在身份認證服務SP.IdentityService中的AuthorizationServiceImpl實現類中的AddUserAsync方法中發布一個用戶注冊成功的事件。我們可以在用戶注冊成功后,使用RabbitMQ的生產者發送一個消息到指定的隊列。補充代碼如下:

// more codepublic async Task<long> AddUserAsync(UserAddRequest user)
{// more code// 發送mq,設配默認幣種MqPublisher publisher = new MqPublisher(newUser.Id.ToString(),MqExchange.UserConfigExchange,MqRoutingKey.UserConfigDefaultCurrencyRoutingKey,MqQueue.UserConfigQueue,MessageType.UserConfigDefaultCurrency,ExchangeType.Direct);await _rabbitMqMessage.SendAsync(publisher);// more code
}

在這段代碼中,我們創建了一個MqPublisher對象,并設置了相關的交換機、路由鍵和隊列。然后,我們使用_rabbitMqMessage.SendAsync(publisher)方法將消息發送到RabbitMQ。

Tip:MqPublisher的實現代碼在SP.Common項目中,它是一個簡單的消息發布者,用于發送消息到RabbitMQ。它包含了交換機、路由鍵、隊列和消息類型等信息。

接下來,我們需要在配置服務SP.ConfigService中訂閱這個事件,并在收到事件時創建默認配置項。我們可以在配置服務的啟動類中添加一個RabbitMQ的消費者來處理這個事件。實現代碼如下:

using SP.Common.ExceptionHandling.Exceptions;
using SP.Common.Message.Model;
using SP.Common.Message.Mq;
using SP.Common.Message.Mq.Model;
using SP.ConfigService.Service;namespace SP.ConfigService.Mq;/// <summary>
/// 用戶配置默認幣種消息消費者服務
/// </summary>
public class UserConfigDefaultCurrencyConsumerService : BackgroundService
{/// <summary>/// RabbitMQ消息處理/// </summary>private readonly RabbitMqMessage _rabbitMqMessage;/// <summary>/// 用戶配置服務/// </summary>private readonly IConfigServer _configServer;/// <summary>/// 日志記錄器/// </summary>private readonly ILogger<UserConfigDefaultCurrencyConsumerService> _logger;/// <summary>/// 配置/// </summary>private readonly IConfiguration _configuration;/// <summary>/// 用戶配置默認幣種消息消費者服務構造函數/// </summary>/// <param name="rabbitMqMessage"></param>/// <param name="logger"></param>/// <param name="configuration"></param>public UserConfigDefaultCurrencyConsumerService(RabbitMqMessage rabbitMqMessage, ILogger<UserConfigDefaultCurrencyConsumerService> logger,IConfiguration configuration){_rabbitMqMessage = rabbitMqMessage;_logger = logger;_configuration = configuration;}/// <summary>/// 消費者服務/// </summary>protected override async Task ExecuteAsync(CancellationToken stoppingToken){MqSubscriber subscriber = new MqSubscriber(MqExchange.UserConfigExchange,MqRoutingKey.UserConfigDefaultCurrencyRoutingKey, MqQueue.UserConfigQueue);await _rabbitMqMessage.ReceiveAsync(subscriber, async message =>{MqMessage mqMessage = message as MqMessage;if (mqMessage == null){_logger.LogError("消息轉換失敗");throw new ArgumentNullException(nameof(mqMessage));}string userId = mqMessage.Body;if (string.IsNullOrEmpty(userId)){_logger.LogError("用戶ID不能為空");throw new BusinessException(nameof(userId));}_logger.LogInformation($"接收到用戶配置默認幣種消息,用戶ID: {userId}");if (!long.TryParse(userId, out long parsedUserId)){_logger.LogError("用戶ID格式錯誤");throw new BusinessException("用戶ID格式錯誤");}// 設置用戶默認幣種,默認幣種id從配置文件中獲取string defaultCurrencyId = _configuration["DefaultCurrencyId"];_logger.LogInformation($"nacos中配置的默認幣種ID: {defaultCurrencyId}");// 調用幣種服務設置用戶默認幣種await _configServer.SetUserDefaultCurrencyAsync(parsedUserId,defaultCurrencyId);});}
}

在這個消費者服務中,我們首先創建了一個MqSubscriber對象,并設置了相關的交換機、路由鍵和隊列。然后,我們使用_rabbitMqMessage.ReceiveAsync(subscriber, async message => { ... })方法來接收消息。當收到消息時,我們將消息體轉換為MqMessage對象,并從中獲取用戶ID。接著,我們調用配置服務的SetUserDefaultCurrencyAsync方法來設置用戶的默認幣種。
SetUserDefaultCurrencyAsync方法中,我們可以實現設置用戶默認幣種的邏輯。
IConfigServer接口中添加方法定義:

/// <summary>
/// 設置用戶默認貨幣
/// </summary>
/// <param name="userId"></param>
/// <param name="defaultCurrencyId"></param>
/// <returns></returns>
Task SetUserDefaultCurrencyAsync(long userId, string defaultCurrencyId);

ConfigServerImpl實現類中實現這個方法:

/// <summary>
/// 設置用戶默認貨幣
/// </summary>
/// <param name="userId"></param>
/// <param name="defaultCurrencyId"></param>
/// <returns></returns>
public Task SetUserDefaultCurrencyAsync(long userId, string defaultCurrencyId)
{Config userConfig = new Config();// 更新默認貨幣IDuserConfig.Value = defaultCurrencyId;userConfig.UserId = userId;userConfig.ConfigType = ConfigTypeEnum.Currency;userConfig.Id = Snow.GetId();userConfig.CreateDateTime = DateTime.Now;userConfig.CreateUserId = userId;_context.Configs.Add(userConfig);// 保存到數據庫_context.SaveChanges();return Task.CompletedTask;
}

在這個方法中,我們創建了一個新的Config對象,并設置其值為默認幣種ID。然后,我們將該配置項添加到數據庫中,并保存更改。

三、總結

在本節中,我們實現了用戶配置服務的核心功能,包括獲取所有配置和更新配置。我們還通過事件機制實現了用戶注冊后自動設置默認幣種的功能。這些功能為孢子記賬應用提供了基礎的用戶配置管理能力,使得用戶可以根據自己的需求進行個性化設置。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/90956.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/90956.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/90956.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python實現PDF按頁分割:靈活拆分文檔的技術指南

Python實現PDF按頁分割&#xff1a;靈活拆分文檔的技術指南 PDF文件處理是日常工作中的常見需求&#xff0c;特別是當我們需要將大型PDF文檔拆分為多個部分時。本文將介紹如何使用Python創建一個靈活的PDF分割工具&#xff0c;能夠根據用戶指定的頁數范圍任意分割文檔。 需求分…

「iOS」——GCD其他方法詳解

GCD學習GCD其他方法dispatch_semaphore &#xff08;信號量&#xff09;**什么是信號量**dispatch_semaphore主要作用dispatch_semaphore主要作用異步轉同步設置一個最大開辟的線程數加鎖機制dispatch_time_t 兩種形式GCD一次性代碼(只執行一次)dispatch_barrier_async/sync柵欄…

【圖像處理基石】如何實現一個車輛檢測算法?

基于AI的車牌檢測和識別算法 問題描述、應用場景與難點 問題描述 車牌檢測和識別是計算機視覺領域的一個特定任務&#xff0c;主要包含兩個核心步驟&#xff1a; 車牌檢測&#xff1a;從圖像中準確定位車牌的位置和區域車牌識別&#xff1a;對檢測到的車牌區域進行字符識別&…

計算機學報 2025年 區塊鏈論文 錄用匯總 附pdf下載

計算機學報 Year&#xff1a;2025 2024請看 1 Title: 基于區塊鏈的動態多云多副本數據完整性審計方法研究 Authors: Key words: 區塊鏈&#xff1b;云存儲&#xff1b;多云多副本存儲&#xff1b;數據完整性審計 Abstract: 隨著云計算技術的快速發展和云存儲服務的日益…

計算機網絡-UDP協議

UDP&#xff08;用戶數據報協議&#xff09;是傳輸層的一種無連接、不可靠、輕量級的協議&#xff0c;適用于對實時性要求高、能容忍少量數據丟失的場景&#xff08;如視頻流、DNS查詢等&#xff09;。以下是UDP的詳細解析&#xff1a;1. UDP的核心特點特性說明無連接通信前無需…

子域名收集和c段查詢

子域名收集方法一、sitesite&#xff1a; 要查詢的域名可以查到相關網站二、oneforall &#xff08;子域名查找工具&#xff09;下載后解壓的文件夾在當前文件夾打開終端然后運行命令 python oneforall.py --target xxxxxxxx&#xff08;這里放你要查的網址&#xff09; run最…

計網-TCP擁塞控制

TCP的擁塞控制&#xff08;Congestion Control&#xff09;是核心機制之一&#xff0c;用于動態調整發送方的數據傳輸速率&#xff0c;避免網絡因過載而出現性能急劇下降&#xff08;如丟包、延遲激增&#xff09;。其核心思想是探測網絡可用帶寬&#xff0c;并在擁塞發生時主動…

依賴倒置原則 Dependency Inversion Principle - DIP

基本知識 1.依賴倒置原則&#xff08;DIP&#xff09;是面向對象設計&#xff08;OOD&#xff09;中的五個基本原則之一&#xff0c;通常被稱為 SOLID 原則中的 D 2.核心思想&#xff1a; 高層模塊不應該依賴低層模塊&#xff0c;兩者都應該依賴抽象。 (High-level modules sho…

原生input添加刪除圖標類似vue里面移入顯示刪除[jquery]

<input type"text" id"servicer-search" class"form-control" autocomplete"off" />上面是剛開始的input <div class"servicer-search-box"><input type"text" id"servicer-search" cla…

整理分享 | Photoshop 2025 (v26.5) 安裝記錄

導語&#xff1a; 最近整理資源時&#xff0c;發現有朋友在找新版 Photoshop。正好手邊有 Photoshop 2025年7月的版本&#xff08;v26.5&#xff09;&#xff0c;就記錄下來分享給大家&#xff0c;供有需要的朋友參考。關于這個版本&#xff1a;這個 Photoshop v26.5 安裝包&am…

【Redis】Redis 數據存儲原理和結構

一、Redis 存儲結構 1.1 KV結構 Redis 本質上是一個 Key-Value&#xff08;鍵值對&#xff0c;KV&#xff09;數據庫&#xff0c;在它豐富多樣的數據結構底層&#xff0c;都基于一種統一的鍵值對存儲結構來進行數據的管理和操作 Redis 使用一個全局的哈希表來管理所有的鍵值對…

【RAG優化】深度剖析OCR錯誤,從根源修復RAG應用的識別問題

1. 引言:OCR——RAG系統中的關鍵問題 當我們將一個包含掃描頁面的PDF或一張報告截圖扔給RAG系統時,我們期望它能“讀懂”里面的內容。這個“讀懂”的第一步,就是OCR。然而,OCR過程并非100%準確,它受到圖像質量、文字布局、字體、語言等多種因素的影響。 一個看似微不足道…

【第六節】方法與事件處理器

方法與事件處理器 方法處理器 可以用 v-on 指令監聽 DOM 事件: <div id="example"> <button v-on:click="greet">Greet</button></div>綁定一個單擊事件處理器到一個方法 greet 。下面在 Vue 實例中定義這個方法 var vm=new V…

大語言模型Claude 4簡介

Anthropic公司成立于2021年&#xff0c;由一群OpenAI前員工組成。他們最新發布的大語言模型(Large Language Model, LLM) Claude 4系列包括兩個版本&#xff1a;Claude Opus 4和Claude Sonnet 4&#xff1a;(1).Claude Sonnet 4&#xff1a;是Claude Sonnet 3.7的升級&#xff…

國產化PDF處理控件Spire.PDF教程:Python 將 PDF 轉換為 Markdown (含批量轉換示例)

PDF 是數字文檔管理的普遍格式&#xff0c;但其固定布局特性限制了在需要靈活編輯、更新或現代工作流集成場景下的應用。相比之下&#xff0c;Markdown&#xff08;.md&#xff09;語法輕量、易讀&#xff0c;非常適合網頁發布、文檔編寫和版本控制。 E-iceblue旗下Spire系列產…

PDF轉Markdown - Python 實現方案與代碼

PDF作為廣泛使用的文檔格式&#xff0c;轉換為輕量級標記語言Markdown后&#xff0c;可無縫集成到技術文檔、博客平臺和版本控制系統中&#xff0c;提高內容的可編輯性和可訪問性。本文將詳細介紹如何使用國產Spire.PDF for Python 庫將 PDF 文檔轉換為 Markdown 格式。 技術優…

深度解析 inaSpeechSegmenter:高效音頻語音分割與檢測開源工具

項目簡介 inaSpeechSegmenter 是法國國家視聽研究院(INA)開源的音頻分割與檢測工具,專為廣播、播客、采訪、影視等多媒體內容的自動化處理設計。它能夠高效地將長音頻自動分割為語音、音樂、噪聲、靜音等片段,并支持性別檢測(男聲/女聲),為后續的語音識別、內容檢索、轉…

VirtualBox安裝Ubuntu 22.04后終端無法打開的解決方案

問題現象在VirtualBox中使用"快速安裝"模式安裝Ubuntu 22.04后圖形終端&#xff08;gnome-terminal&#xff09;無法通過圖標或快捷鍵(CtrlAltT)啟動系統其他功能正常根本原因語言環境(Locale)配置異常導致&#xff1a;快速安裝模式可能跳過Locale生成步驟gnome-term…

java磁盤操作與IO流(序列化、Properties類)

目錄 一、磁盤操作 1、File類&#xff1a; &#xff08;1&#xff09;創建File對象&#xff1a; &#xff08;2&#xff09;獲取文件信息&#xff1a; &#xff08;3&#xff09;判斷文件 &#xff08;4&#xff09;刪除文件 &#xff08;5&#xff09;創建文件&#xff…

【WPF】WPF Prism 開發經驗總結:菜單命令刪除項時報 InvalidCastException 的問題分析與解決

WPF Prism 開發經驗總結&#xff1a;菜單命令刪除項時報 InvalidCastException 的問題分析與解決 在 WPF Prism 項目中使用 ContextMenu 執行刪除操作時&#xff0c;遇到一個令人疑惑的問題&#xff1a;命令綁定本身沒有問題&#xff0c;但點擊“刪除”菜單后&#xff0c;程序拋…