33.【.NET8 實戰--孢子記賬--從單體到微服務--轉向微服務】--單體轉微服務--財務服務--記賬

這篇文章我們一起把記賬模塊從單體應用遷移到微服務架構中。記賬模塊的功能想必大家都已經了解了,主要是記錄用戶的收入和支出,以及對這些記錄的刪除修改和查詢等操作。具體的功能可以參考單體應用專欄,在這里就不多講了。我們現在一起開始遷移記賬模塊的代碼吧。

一、小修改

和前面的功能一樣,我們把記賬模塊的代碼從單體應用中抽離出來,放到微服務項目中。但是需要做一些小修改,這些修改包括前面幾篇文章中提到的將接口修改為標準的restful接口、將Controller繼承自ControllerBase而不是BaseController,以及調整路由地址等,在記賬模塊中,還要對新增記賬、修改記賬、刪除記賬這三個功能進行調整。我們先來看一下單體應用中這三個功能在Service層中的代碼:

// more code .../// <summary>
/// 收支記錄實現類
/// </summary>
public class IncomeExpenditureRecordImp : IIncomeExpenditureRecordServer
{// more code .../// <summary>/// 新增收支記錄/// </summary>/// <param name="incomeExpenditureRecord"></param>public void Add(IncomeExpenditureRecord incomeExpenditureRecord){//開啟事務using (var transaction = _sporeAccountingDbContext.Database.BeginTransaction()){try{// 查找記錄范圍內的預算var budget = _sporeAccountingDbContext.Budgets.FirstOrDefault(x => x.UserId == incomeExpenditureRecord.UserId&& x.StartTime <= incomeExpenditureRecord.RecordDate &&x.EndTime >= incomeExpenditureRecord.RecordDate&& x.IncomeExpenditureClassificationId ==incomeExpenditureRecord.IncomeExpenditureClassificationId);if (budget != null){// 查詢分類var classification = _sporeAccountingDbContext.IncomeExpenditureClassifications.FirstOrDefault(x => x.Id == incomeExpenditureRecord.IncomeExpenditureClassificationId);if (classification.Type== IncomeExpenditureTypeEnmu.Income){budget.Remaining -= incomeExpenditureRecord.AfterAmount;// 獲取包含支出記錄記錄日期的報表記錄var reports = _sporeAccountingDbContext.Reports.Where(x => x.UserId == incomeExpenditureRecord.UserId&& x.Year <= incomeExpenditureRecord.RecordDate.Year &&x.Month >= incomeExpenditureRecord.RecordDate.Month &&x.ClassificationId ==incomeExpenditureRecord.IncomeExpenditureClassificationId);// 如果沒有就說明程序還未將其寫入報表,那么就不做任何處理for (int i = 0; i < reports.Count(); i++){var report = reports.ElementAt(i);report.Amount += incomeExpenditureRecord.AfterAmount;_sporeAccountingDbContext.Reports.Update(report);}}_sporeAccountingDbContext.Budgets.Update(budget);}_sporeAccountingDbContext.IncomeExpenditureRecords.Add(incomeExpenditureRecord);_sporeAccountingDbContext.SaveChanges();//提交事務transaction.Commit();}catch (Exception e){//回滾事務transaction.Rollback();throw;}}}/// <summary>/// 刪除收支記錄/// </summary>/// <param name="incomeExpenditureRecordId"></param>/// <returns></returns>public void Delete(string incomeExpenditureRecordId){//開啟事務using (var transaction = _sporeAccountingDbContext.Database.BeginTransaction()){try{var incomeExpenditureRecord = _sporeAccountingDbContext.IncomeExpenditureRecords.FirstOrDefault(x => x.Id == incomeExpenditureRecordId);if (incomeExpenditureRecord != null){// 查找記錄范圍內的預算var budget = _sporeAccountingDbContext.Budgets.FirstOrDefault(x => x.UserId == incomeExpenditureRecord.UserId&& x.StartTime <= incomeExpenditureRecord.RecordDate &&x.EndTime >= incomeExpenditureRecord.RecordDate&& x.IncomeExpenditureClassificationId == incomeExpenditureRecord.IncomeExpenditureClassificationId);if (budget != null){// 查詢分類var classification = _sporeAccountingDbContext.IncomeExpenditureClassifications.FirstOrDefault(x => x.Id == incomeExpenditureRecord.IncomeExpenditureClassificationId);if (classification.Type== IncomeExpenditureTypeEnmu.Income){budget.Remaining += incomeExpenditureRecord.AfterAmount;// 獲取包含支出記錄記錄日期的報表記錄var reports = _sporeAccountingDbContext.Reports.Where(x => x.UserId == incomeExpenditureRecord.UserId&& x.Year <= incomeExpenditureRecord.RecordDate.Year &&x.Month >= incomeExpenditureRecord.RecordDate.Month &&x.ClassificationId ==incomeExpenditureRecord.IncomeExpenditureClassificationId);// 如果沒有就說明程序還未將其寫入報表,那么就不做任何處理for (int i = 0; i < reports.Count(); i++){var report = reports.ElementAt(i);report.Amount -= incomeExpenditureRecord.AfterAmount;_sporeAccountingDbContext.Reports.Update(report);}}_sporeAccountingDbContext.Budgets.Update(budget);}_sporeAccountingDbContext.IncomeExpenditureRecords.Remove(incomeExpenditureRecord);_sporeAccountingDbContext.SaveChanges();//提交事務transaction.Commit();}}catch (Exception e){//回滾事務transaction.Rollback();throw;}}}/// <summary>/// 修改收支記錄/// </summary>/// <param name="incomeExpenditureRecord"></param>/// <returns></returns>public void Update(IncomeExpenditureRecord incomeExpenditureRecord){using (var transaction = _sporeAccountingDbContext.Database.BeginTransaction()){try{// 查詢原記錄var oldIncomeExpenditureRecord = _sporeAccountingDbContext.IncomeExpenditureRecords.FirstOrDefault(x => x.Id == incomeExpenditureRecord.Id);// 查找記錄范圍內的預算var budget = _sporeAccountingDbContext.Budgets.FirstOrDefault(x => x.UserId == incomeExpenditureRecord.UserId&& x.StartTime <= incomeExpenditureRecord.RecordDate &&x.EndTime >= incomeExpenditureRecord.RecordDate&& x.IncomeExpenditureClassificationId ==incomeExpenditureRecord.IncomeExpenditureClassificationId);if (budget != null){// 查詢分類var classification = _sporeAccountingDbContext.IncomeExpenditureClassifications.FirstOrDefault(x => x.Id == incomeExpenditureRecord.IncomeExpenditureClassificationId);if (classification.Type== IncomeExpenditureTypeEnmu.Income){//如果是支出,需要減去原來的金額budget.Remaining = (budget.Amount - incomeExpenditureRecord.AfterAmount);// 根據舊的支出記錄判斷是否修改了記錄日期// 如果是修改了記錄日期,那么就將原記錄日期所在的報表對應的分類金額減去,將新記錄日期所在報表對應的分類金額加上if (oldIncomeExpenditureRecord.RecordDate != incomeExpenditureRecord.RecordDate){// 獲取包含支出記錄記錄日期的報表記錄var oldReports = _sporeAccountingDbContext.Reports.Where(x => x.UserId == incomeExpenditureRecord.UserId&& x.Year <= oldIncomeExpenditureRecord.RecordDate.Year &&x.Month >= oldIncomeExpenditureRecord.RecordDate.Month &&x.ClassificationId == oldIncomeExpenditureRecord.IncomeExpenditureClassificationId);// 如果沒有就說明程序還未將其寫入報表,那么就不做任何處理for (int i = 0; i < oldReports.Count(); i++){var oldReport = oldReports.ElementAt(i);oldReport.Amount -= oldIncomeExpenditureRecord.AfterAmount;_sporeAccountingDbContext.Reports.Update(oldReport);}// 獲取包含支出記錄記錄日期的報表記錄var newReport = _sporeAccountingDbContext.Reports.Where(x => x.UserId == incomeExpenditureRecord.UserId&& x.Year <= incomeExpenditureRecord.RecordDate.Year &&x.Month >= incomeExpenditureRecord.RecordDate.Month &&x.ClassificationId ==incomeExpenditureRecord.IncomeExpenditureClassificationId);// 如果沒有就說明程序還未將其寫入報表,那么就不做任何處理for (int i = 0; i < newReport.Count(); i++){var report = newReport.ElementAt(i);report.Amount += incomeExpenditureRecord.AfterAmount;_sporeAccountingDbContext.Reports.Update(report);}}}else{//如果是收入,需要加上原來的金額budget.Remaining = (budget.Amount + incomeExpenditureRecord.AfterAmount);}_sporeAccountingDbContext.Budgets.Update(budget);}oldIncomeExpenditureRecord.AfterAmount = incomeExpenditureRecord.AfterAmount;oldIncomeExpenditureRecord.BeforAmount = incomeExpenditureRecord.BeforAmount;oldIncomeExpenditureRecord.RecordDate = incomeExpenditureRecord.RecordDate;oldIncomeExpenditureRecord.Remark = incomeExpenditureRecord.Remark;oldIncomeExpenditureRecord.CurrencyId = incomeExpenditureRecord.CurrencyId;oldIncomeExpenditureRecord.IncomeExpenditureClassificationId =incomeExpenditureRecord.IncomeExpenditureClassificationId;_sporeAccountingDbContext.IncomeExpenditureRecords.Update(oldIncomeExpenditureRecord);_sporeAccountingDbContext.SaveChanges();//提交事務transaction.Commit();}catch (Exception e){//回滾事務transaction.Rollback();throw;}}}// more code ...
}

在上面的代碼中,我們看到這三個功能,都有兩個共同的操作:一個是對預算的操作,另一個是對報表的操作。在這個單體應用代碼中,我們都是直接調用預算和報表的DbSet進行操作,并且啟用了事務來保證數據的一致性。這里看似合理,但是實際上并不符合微服務的設計原則,因為微服務應該是獨立的,不能直接操作其他服務的數據,同時我們也要保證方法的單一原則,因此在這三個方法里操作其他數據是不合理的。并且,如果我們存儲記賬記錄時,預算數據或者報表數據沒有存儲成功,就會觸發回滾操作,這樣就會導致記賬記錄沒有存儲成功,對于這種設計來不符合用戶體驗,因此我們需要對這三個方法進行修改。

我們要做的是將預算增刪操作抽離出來,讓新增記賬、修改記賬、刪除記賬這三個功能在每次存儲數據成功后,發送MQ消息,然后預算訂閱MQ消息,進行相應的增刪改操作。這樣就可以保證記賬模塊的獨立性,同時也能保證數據的一致性,即使是預算服務出現問題,也不會影響到記賬模塊的正常運行。修改后的Service層代碼如下:

// more code .../// <summary>
/// 記賬服務實現類
/// </summary>
public class AccountingServerImpl : IAccountingServer
{/// <summary>/// RabbitMQ消息處理/// </summary>private readonly RabbitMqMessage _rabbitMqMessage;/// <summary>/// 新增記賬/// </summary>/// <param name="accountBookId">賬本ID</param>/// <param name="request">記賬添加請求</param>/// <returns></returns>public long Add(long accountBookId, AccountingAddRequest request){// more code ...//通過MQ發送記賬數據到消息隊列,從預算中扣除金額MqPublisher mqPublisher = new MqPublisher(accounting.AfterAmount.ToString("F2"),MqExchange.BudgetExchange,MqRoutingKey.BudgetRoutingKey,MqQueue.BudgetQueue, MessageType.BudgetDeduct, ExchangeType.Direct);_rabbitMqMessage.SendAsync(mqPublisher).Start();// 返回新增的記賬IDreturn accounting.Id;}/// <summary>/// 刪除記賬/// </summary>/// <param name="accountBookId">賬本ID</param>/// <param name="id">記賬ID</param>public void Delete(long accountBookId, long id){// more code ...//通過MQ發送刪除記賬數據到消息隊列,把預算中的金額恢復MqPublisher mqPublisher = new MqPublisher(accounting.AfterAmount.ToString("F2"),MqExchange.BudgetExchange,MqRoutingKey.BudgetRoutingKey,MqQueue.BudgetQueue, MessageType.BudgetAdd, ExchangeType.Direct);_rabbitMqMessage.SendAsync(mqPublisher).Start();}/// <summary>/// 修改記賬/// </summary>/// <param name="accountBookId">賬本ID</param>/// <param name="request">修改請求</param>public void Edit(long accountBookId, AccountingEditRequest request){// more code ...// 通過MQ發送修改記賬數據到消息隊列,更新預算中的金額MqPublisher mqPublisher = new MqPublisher(amountDifference.ToString("F2"),MqExchange.BudgetExchange,MqRoutingKey.BudgetRoutingKey,MqQueue.BudgetQueue, MessageType.BudgetUpdate, ExchangeType.Direct);_rabbitMqMessage.SendAsync(mqPublisher).Start();}// more code ...
}

在上面的代碼中,我們對新增記賬、刪除記賬、修改記賬這三個方法進行了修改。新增記賬時,我們將記賬數據發送到MQ消息隊列中,預算服務會訂閱這個消息,并進行相應的扣款操作。刪除記賬時,我們同樣發送消息到MQ消息隊列中,預算服務會將金額恢復到預算中。修改記賬時,我們計算出原金額與新金額的差額,并發送到MQ消息隊列中,預算服務會根據差額進行相應的更新操作。下面是新增的預算訂閱MQ消息處理類的代碼:

using SP.Common.Message.Model;
using SP.Common.Message.Mq;
using SP.Common.Message.Mq.Model;
using SP.FinanceService.Models.Entity;
using SP.FinanceService.Models.Enumeration;
using SP.FinanceService.Service;namespace SP.FinanceService.Mq;/// <summary>
/// Budget 消息消費者服務
/// </summary>
public class BudgetConsumerService : BackgroundService
{/// <summary>/// RabbitMq 消息/// </summary>private readonly RabbitMqMessage _rabbitMqMessage;/// <summary>/// 日志記錄器/// </summary>private readonly ILogger<BudgetConsumerService> _logger;/// <summary>/// 預算服務/// </summary>private readonly IBudgetServer _budgetService;/// <summary>///  Budget 消息消費者服務/// </summary>/// <param name="rabbitMqMessage"></param>/// <param name="logger"></param>/// <param name="budgetService"></param>public BudgetConsumerService(RabbitMqMessage rabbitMqMessage, ILogger<BudgetConsumerService> logger,IBudgetServer budgetService){_logger = logger;_rabbitMqMessage = rabbitMqMessage;_budgetService = budgetService;}/// <summary>/// RabbitMq 消息/// </summary>/// <param name="stoppingToken"></param>/// <returns></returns>protected override async Task ExecuteAsync(CancellationToken stoppingToken){MqSubscriber subscriber = new MqSubscriber(MqExchange.BudgetExchange,MqRoutingKey.BudgetRoutingKey, MqQueue.BudgetQueue);await _rabbitMqMessage.ReceiveAsync(subscriber, async message =>{// 驗證消息MqMessage mqMessage = ValidateMessage(message);if (mqMessage == null) return;// 獲取當前預算List<Budget> budgets = GetCurrentBudgets();if (budgets == null || budgets.Count == 0) return;decimal amount = decimal.Parse(message.Body);// 根據消息類型處理預算switch (mqMessage.Type){case MessageType.BudgetAdd:_logger.LogInformation("接收到預算增加處理消息, {Message}", mqMessage);UpdateBudgetsByAmount(budgets, amount, true);break;case MessageType.BudgetUpdate:_logger.LogInformation("接收到預算更新消息, {Message}", mqMessage);UpdateBudgetsByAmount(budgets, amount, true);break;case MessageType.BudgetDeduct:_logger.LogInformation("接收到預算扣除消息, {Message}", mqMessage);UpdateBudgetsByAmount(budgets, amount, false);break;default:_logger.LogWarning("未知的消息類型: {Type}", mqMessage.Type);break;}await Task.CompletedTask;});}/// <summary>/// 驗證消息/// </summary>/// <param name="message">原始消息</param>/// <returns>驗證后的 MqMessage,如果驗證失敗返回 null</returns>private MqMessage ValidateMessage(object message){MqMessage mqMessage = message as MqMessage;if (mqMessage == null){_logger.LogError("消息轉換失敗");return null;}return mqMessage;}/// <summary>/// 獲取當前預算/// </summary>/// <returns>當前預算列表,如果沒有可用預算返回空列表</returns>private List<Budget> GetCurrentBudgets(){List<Budget> budgets = _budgetService.QueryCurrentBudgets();if (budgets == null || budgets.Count == 0){_logger.LogInformation("當前沒有可用的預算,不執行處理");return new List<Budget>();}return budgets;}/// <summary>/// 根據金額更新預算/// </summary>/// <param name="budgets">預算列表</param>/// <param name="amount">金額</param>/// <param name="isAdd">是否為增加操作,true為增加,false為扣除</param>private void UpdateBudgetsByAmount(List<Budget> budgets, decimal amount, bool isAdd){string operation = isAdd ? "增加" : "扣除";decimal operationAmount = isAdd ? amount : -amount;// 更新月度預算UpdateBudgetByPeriod(budgets, PeriodEnum.Month, operationAmount, operation);// 更新年度預算UpdateBudgetByPeriod(budgets, PeriodEnum.Year, operationAmount, operation);// 更新季度預算UpdateBudgetByPeriod(budgets, PeriodEnum.Quarter, operationAmount, operation);// 保存更改到數據庫_budgetService.UpdateBudgets(budgets);}/// <summary>/// 根據周期更新預算/// </summary>/// <param name="budgets">預算列表</param>/// <param name="period">預算周期</param>/// <param name="amount">金額變化</param>/// <param name="operation">操作類型描述</param>private void UpdateBudgetByPeriod(List<Budget> budgets, PeriodEnum period, decimal amount, string operation){Budget budget = budgets.FirstOrDefault(b => b.Period == period);if (budget != null){budget.Amount += amount;string periodName = GetPeriodName(period);_logger.LogInformation("{PeriodName}預算{Operation}成功,{Operation}金額: {Amount}", periodName, operation, operation, budget.Amount);}}/// <summary>/// 獲取周期名稱/// </summary>/// <param name="period">預算周期</param>/// <returns>周期名稱</returns>private string GetPeriodName(PeriodEnum period){return period switch{PeriodEnum.Month => "月度",PeriodEnum.Year => "年度",PeriodEnum.Quarter => "季度",_ => "未知"};}
}

在上面的代碼中,我們創建了一個BudgetConsumerService類,它繼承自BackgroundService,用于處理預算相關的MQ消息。我們在ExecuteAsync方法中訂閱了MQ消息,并根據消息類型進行相應的處理。我們還添加了一些日志記錄,以便于調試和監控。

對于調用報表的部分,我們暫時先將這部分代碼刪除掉,因為這個設計其實是不符合業務邏輯的,我們的報表都是定時生成的,而不是實時生成的,因此對報表的修改其實是沒必要的,因此我們在這里不對報表進行任何操作。等到后面我們實現了報表模塊后,再來處理報表相關的邏輯。

二、總結

在這篇文章中,我們將記賬模塊從單體應用遷移到微服務架構中,并對新增記賬、刪除記賬、修改記賬這三個功能進行了調整。我們將預算的增刪改操作抽離出來,讓記賬模塊通過MQ消息與預算服務進行交互,從而實現了模塊的獨立性和數據的一致性。我們還創建了一個BudgetConsumerService類,用于處理預算相關的MQ消息。

通過這些修改,我們使得記賬模塊能夠更好地適應微服務架構的設計原則,同時也提高了系統的可維護性和可擴展性。接下來,我們將繼續實現記賬模塊的其他功能,并將其與其他模塊進行集成。

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

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

相關文章

Cursor結合Playwright MCP Server支持自動化

Cursor結合Playwright MCP Server支持自動化 今天分享一下 playwright MCP Server&#xff0c;其提供了瀏覽器自動化能力&#xff0c;使大型語言模型能夠在真實的瀏覽器環境中與網頁交互&#xff0c; 也可以執行任務&#xff0c;例如運行JavaScript、截屏和導航網頁元素&…

Python 求梯形面積的程序(Program to find area of a Trapezoid)

梯形的定義&#xff1a; 梯形是凸四邊形&#xff0c;至少有一對邊平行。平行邊稱為梯形的底邊&#xff0c;另外兩條不平行的邊稱為梯形的腿。梯形也可以有兩對底邊。在上圖中&#xff0c;CD || AB&#xff0c;它們構成底邊&#xff0c;而另外兩條邊&#xff0c;即AD和BC&#…

C語言 —— 指針(4)

動態內存分配動態內存需要手動申請&#xff0c;手動歸還&#xff0c;其內存是開辟在堆區。申請的函數為&#xff1a;void *malloc(size_t size) &#xff08;需包含頭文件#include<stdlib.h>&#xff09;size&#xff1a;要分配的內存大小&#xff0c;以字節為單位。申請…

常用算法思想及模板

今天繼續整理一些關于算法競賽中C適用的一些模板以及思想。 保留x位小數 保留x位小數在C語言中可以使用printf中的"%.xf"來實現&#xff0c;但是很多C選手由于關閉了同步流&#xff0c;害怕cin、cout與scanf、printf混用容易出錯&#xff0c;所以就給大家介紹一個強…

GitLab 倉庫 — 常用的 git 命令

在公司的 gitlab 公共倉庫中寫代碼做項目時&#xff0c;主要涉及以下常用 git 命令&#xff1a;一、單個命令講解1. 拉取代碼&#xff08;1&#xff09;git clone [倉庫 URL]?克隆遠程倉庫到本地&#xff08;需確保 URL 正確&#xff09; ?&#xff08;?2&#xff09;git pu…

【28】C# WinForm入門到精通 ——多文檔窗體MDI【屬性、方法、實例、源碼】【多窗口重疊、水平平鋪、垂直平鋪、窗體傳值】

文章目錄1多文檔窗體MDI2 基本設置3 實例&#xff1a;多窗口重疊、水平平鋪、垂直平鋪3.1 主窗口屬性設置3.2 主窗口3.3 主窗口窗口添加MenuStrip菜單3.4 添加處理函數3.5 測試效果4 利用窗體參數定義進行傳值4.1 在Form2、Form3添加相關控件4.2 Form3 定義函數public Form3(st…

【計算機科學與應用】基于Session欺騙攻擊的Web應用程序防護

導讀&#xff1a; 本文對Web應用程序開發中的Session欺騙攻擊進行了闡述&#xff0c;詳細講解了防范Session欺騙攻擊的三種傳統方法&#xff0c;并給出了防范代碼&#xff0c;分析了三種傳統防范方法的不足&#xff0c;新設計了一種通過Referer信息驗證來加強對Session欺騙的防…

yolo8+阿里千問圖片理解(華為簡易版小藝看世界)

? 實現目標 按下空格鍵 → 獲取攝像頭當前畫面&#xff1b; 將圖片上傳給 大模型 接口&#xff0c;讓其“看圖說話”&#xff1b; 獲取返回描述后&#xff0c;以字幕形式展示在圖像畫面上&#xff1b; 持續顯示識別結果&#xff0c;直到下次按空格。 &#x1f9e0; 需要準…

【ee類保研面試】數學類---線性代數

25保研er&#xff0c;希望將自己的面試復習分享出來&#xff0c;供大家參考 part0—英語類 part1—通信類 part2—信號類 part3—高數類 part100—self項目準備 文章目錄線性代數知識點大全**1. 余子式與代數余子式****2. 行列式的含義****3. 矩陣的秩&#xff08;Rank&#xf…

在 Scintilla 中為 Squirrel 語言設置語法解析器的方法

Scintilla 作為一個強大的開源文本編輯控件&#xff0c;通過配置語法解析器&#xff0c;能夠對多種編程語言實現語法高亮、代碼折疊等實用功能。若要為新語言 Squirrel 設置語法解析器&#xff0c;可參考以下步驟&#xff1a;?創建 Lexer 源文件&#xff1a;Scintilla 通過 Le…

Go語言核心知識點補充

Go語言核心知識點補充 make函數、for循環與輸入處理詳解 在前幾章的內容中&#xff0c;我們介紹了Go語言的基礎語法、變量聲明、切片、循環等核心概念。但在實際開發中&#xff0c;一些細節性的知識點往往決定了代碼的健壯性與效率。 本文將針對前幾章涉及到的變量聲明與初始化…

AI服務器中,EEPROM有哪些部件使用,需要存儲哪些信息?

在AI服務器中&#xff0c;EEPROM&#xff08;電可擦可編程只讀存儲器&#xff09;主要用于存儲關鍵組件的配置數據、身份信息和校準參數。以下是主要組件及其存儲內容&#xff1a; 一、核心組件及存儲數據主板&#xff08;Baseboard Management Controller, BMC&#xff09; FR…

It學習資源下載

一.UI 8個高質量UI設計網站&#xff0c;靈感收集必備&#xff01;

Docker Compose :從入門到企業級部署

Docker Compose &#xff1a;從入門到企業級部署1. Docker Compose 核心概念1.1 Compose 架構全景圖2. 完整開發工作流2.1 典型開發流程2.2 多服務示例項目結構3. 核心配置詳解3.1 服務配置矩陣3.2 網絡拓撲示例4. 企業級部署方案4.1 多環境配置管理4.2 擴展部署架構5. 高級技巧…

1.2.vue插值表達式

在 Vue.js 中&#xff0c;插值表達式是用于在模板中顯示數據的一種方式。它使用雙大括號語法 {{ }} 來包裹需要輸出的變量或表達式的值。Vue 會自動將這些表達式的值插入到 HTML 文檔中相應的位置。插值表達式基本用法最基本的插值表達式形式就是直接在模板中引用 Vue 實例中的…

Python數據處理基礎(學習筆記分享)

Python數據處理入門 常用庫學習 numpy NumPy&#xff08;Numerical Python&#xff09; 是 Python 中用于高效數值計算的庫&#xff0c;核心是提供一個強大的 ndarray?&#xff08;多維數組&#xff09;對象&#xff0c;類似于 C/C 中的數組&#xff0c;但支持更豐富的操作&a…

力扣面試150題--顛倒二進制位

Day 89 題目描述思路 二進制的算法&#xff0c;將十進制轉化為二進制&#xff0c;有一點需要注意&#xff0c;直接采取庫函數轉化為二進制再反轉會出現問題&#xff08;這也是為什么我要補0的原因&#xff09;&#xff0c;因為轉化過去不滿足32位的二進制&#xff0c;前面不會當…

【ResNet50圖像分類部署至RK3588】模型訓練→轉換RKNN→開發板部署

已在GitHub開源與本博客同步的ResNet50v2_RK3588_Classificationt項目&#xff0c;地址&#xff1a;https://github.com/A7bert777/ResNet50v2_RK3588_Classification 詳細使用教程&#xff0c;可參考README.md或參考本博客第八章 模型部署 文章目錄一、項目回顧二、模型選擇介…

C# _泛型

目錄 泛型是什么? 泛型的主要優勢 創建一個泛型類 泛型方法 泛型是什么? 泛型是通過參數化來實現同一份代碼上操作多種數據類型 利用參數類型將參數的類型抽象化 從而實現靈活的復用 總結: 通過泛型可以實現在同一份代碼上操作多種數據類型的邏輯 將類和類中的成員定義…

Vue路由鉤子完全指南

Vue.js中的路由導航鉤子&#xff08;Navigation Guards&#xff09;主要用于在路由導航過程中進行攔截和處理&#xff0c;確保訪問控制和狀態管理。以下是主要分類及使用方法&#xff1a; 1. 全局鉤子函數 作用于整個路由實例&#xff0c;需在路由配置外定義&#xff1a; befor…