22. 【.NET 8 实战--孢子记账--从单体到微服务】--记账模块--切换主币种

发布于:2025-02-11 ⋅ 阅读:(65) ⋅ 点赞:(0)

这篇文章我们将结合主币种设置以及收支记录实现切换主币种后重新计算以前记录的转换后的金额。那么,为什么要在切换主币种后要重新计算转换后的金额呢?有以下两个原因:

  1. 统一的币种,方便我们统计数据
  2. 方便用户按照当地的币种查看收支

下面我们就一起来实现吧。

一、实现逻辑

我们先来看一下要实现这个功能所需要的数据:

  1. 新的主币种
  2. 转换前币种
  3. 转换前金额
    接着我们一起把实现的思路写出来。首先我们要修改主币种配置,将旧的主币种替换成新的主币种,主币种配置修改成后,我们会利用消息队列发送一条主币种修改通知,负责重新计算转换后的金额的代码订阅这个通知,当它收到通知后就开始执行重新计算记录转换后的金额。负责重新计算转换后的金额的代码会先去查询当前用户全部记录,然后对账本中每条记录进行重新计算转换后的金额,最后将修改结果更新到数据库。

二、实现

我们根据上一小节的思路,先自己实现切换主币种的全部功能,然后把自己的代码和下面的代码核对一下,看看哪里不一样。这里提醒一下,我们订阅主币种修改通知的代码应放在RabbitMQBackgroundService类中。

2.1 修改主币种

ConfigController

/// <summary>
/// 更新用户配置
/// </summary>
/// <param name="configViewModel"></param>
/// <returns></returns>
[HttpPut]
[Route("Update")]
public ActionResult<ResponseData<bool>> Update([FromBody] ConfigViewModel configViewModel)
{
    try
    {
        string userId = GetUserId();
        bool isExist = _configServer.IsExist(userId, configViewModel.Id);
        if (!isExist)
        {
            return Ok(new ResponseData<bool>(HttpStatusCode.NotFound, "用户配置不存在"));
        }

        string oldValue = _configServer.Query(userId, configViewModel.ConfigTypeEnum).Value;
        _configServer.Update(userId, configViewModel.Id, configViewModel.Value);

        //如果切换的是主币种,那么就将以前的所有金额全部转换成新的主币种的金额
        if (configViewModel.ConfigTypeEnum == ConfigTypeEnum.Currency)
        {
            _ = _rabbitMqPublisher.Publish<MainCurrency>("UpdateConversionAmount", "UpdateConversionAmount",
                new MainCurrency()
                {
                    UserId = userId,
                    Currency = configViewModel.Value,
                    OldCurrency = oldValue
                });
            _ = _rabbitMqPublisher.Publish<MainCurrency>("UpdateBudgetAmount",
                "UpdateBudgetAmount", new MainCurrency()
                {
                    UserId = userId,
                    Currency = configViewModel.Value,
                    OldCurrency = oldValue
                });
        }

        return Ok(new ResponseData<bool>(HttpStatusCode.OK, data: true));
    }
    catch (Exception ex)
    {
        return Ok(new ResponseData<bool>(HttpStatusCode.InternalServerError, "服务器异常"));
    }
}

这段代码的作用是更新用户配置,并在特定条件下触发与主币种切换相关的业务逻辑。首先,代码通过 GetUserId() 方法获取当前操作用户的唯一标识符 userId。随后,通过 _configServer.IsExist 方法检查用户配置是否存在。如果配置不存在,返回包含 NotFound 状态的响应,提示“用户配置不存在”。如果配置存在,调用 _configServer.Update 方法,更新指定的配置值 configViewModel.Value。接着,代码进一步判断 configViewModel.ConfigTypeEnum 是否为 Currency 类型,即当前更新的配置是否为主币种。当更新的配置是主币种时,通过 _rabbitMqPublisher.Publish 方法向消息队列发送一条 UpdateConversionAmount 消息,其中包含用户 Id 和新的主币种Id。这一步的目的是通知其他系统或模块,执行与主币种切换相关的金额转换逻辑。

2.2 重新计算转换后的金额

RabbitMQBackgroundService

//more code

await _subscriberService.SubscribeAsync<MainCurrency>("UpdateConversionAmount", "UpdateConversionAmount",
	async (mainCurrency) =>
	{
	    //1.获取所有收支记录
	    using var scope = _serviceProvider.CreateScope();
	    var recordService = scope.ServiceProvider.GetRequiredService<IIncomeExpenditureRecordServer>();
	    var records = recordService.QueryByUserId(mainCurrency.UserId);
	
	    //2.将所有记录的金额转换为新的主币种(记录中的币种转换为新的主币种)
	    var currencyServer = scope.ServiceProvider.GetRequiredService<ICurrencyServer>();
	    var exchangeRateRecordServer = scope.ServiceProvider.GetRequiredService<IExchangeRateRecordServer>();
	    Currency? query = currencyServer.Query(mainCurrency.Currency);
	    if (query == null)
	    {
	        return;
	    }
	    Currency? oldCurrency = currencyServer.Query(mainCurrency.OldCurrency);
	    if (oldCurrency == null)
	    {
	        return;
	    }
	    //获取预算币种和主币种的汇率
	    ExchangeRateRecord? exchangeRateRecord =
	        exchangeRateRecordServer.Query($"{oldCurrency.Abbreviation}_{query.Abbreviation}");
	    if(exchangeRateRecord == null)
	    {
	        return;
	    }
	    for (int i = 0; i < records.Count; i++)
	    {
	        var record = records[i];
	        if (exchangeRateRecord != null)
	            record.AfterAmount = exchangeRateRecord.ExchangeRate * record.BeforAmount;
	    }
	
	    //3.更新所有记录
	    recordService.UpdateRecord(records);
	});


//more code

这段代码实现了一个异步消息订阅和处理的逻辑,用于在主币种发生变更时更新所有收支记录的金额。代码逻辑通过订阅消息队列中的 UpdateConversionAmount 主题来触发。 核心流程是:通过 _subscriberService.SubscribeAsync 方法订阅名为 UpdateConversionAmount 的消息,并注册一个异步处理函数。在处理函数中,首先从依赖注入容器中获取 IIncomeExpenditureRecordServer 服务,查询出指定用户的所有收支记录。接着,使用 ICurrencyServer 查询新的主币种信息,确保其存在;然后通过 IExchangeRateRecordServer 获取当前记录的币种和新的主币种之间的汇率。如果汇率数据存在,则将记录中的金额 (BeforAmount) 转换为以新的主币种为单位的金额 (AfterAmount)。最后,通过 recordService.UpdateRecord 将更新后的记录批量保存到数据库中。

三、总结

通过主币种切换的业务场景,探讨了如何在用户更改主币种配置后重新计算历史收支记录的转换金额。这一功能的实现基于两个核心需求:统一币种便于统计数据,以及方便用户按照本地币种查看收支信息。实现过程中,首先修改主币种配置,当用户设置新的主币种后,系统通过消息队列发送变更通知。相关处理逻辑通过 RabbitMQBackgroundService 订阅该通知,触发重新计算金额的操作。具体流程包括:查询当前用户的所有收支记录,获取新的主币种和原币种之间的汇率,并根据汇率计算每条记录的转换金额,最后更新数据库中的记录。
该方案采用分层设计和异步消息机制,将主币种的配置管理与金额转换逻辑解耦,提升了系统的可扩展性和维护性。整体流程清晰,代码实现规范,为类似场景的开发提供了良好的参考。


网站公告

今日签到

点亮在社区的每一天
去签到