这篇文章我们一起把记账模块从单体应用迁移到微服务架构中。记账模块的功能想必大家都已经了解了,主要是记录用户的收入和支出,以及对这些记录的删除修改和查询等操作。具体的功能可以参考单体应用专栏,在这里就不多讲了。我们现在一起开始迁移记账模块的代码吧。

一、小修改

和前面的功能一样,我们把记账模块的代码从单体应用中抽离出来,放到微服务项目中。但是需要做一些小修改,这些修改包括前面几篇文章中提到的将接口修改为标准的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…