当前位置: 首页 > news >正文

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.xdnf.cn/news/1197343.html

相关文章:

  • Java中排序规则详解
  • solidity从入门到精通 第六章:安全第一
  • vmware虚拟机中 ubuntu 20.04通过nat设置静态ip(固定ip)
  • Java学习-------桥接模式
  • 文件权限标记机制在知识安全共享中的应用实践
  • 通信名词解释:I2C、USART、SPI、RS232、RS485、CAN、TCP/IP、SOCKET、modbus
  • 基于开源AI智能名片链动2+1模式S2B2C商城小程序的人格品牌化实现路径研究
  • Linux-文件与文本管理
  • 嵌入式软件面试八股文
  • Photo Studio PRO 安卓版:专业级照片编辑的移动解决方案
  • STM32-USART串口实现接收数据三种方法(1.根据\r\n标志符、2.空闲帧中断、3.根据定时器辅助接收)
  • 将远程 main 分支同步到 develop 分支的完整指南
  • 深入理解指针(三)
  • 用 Flask 打造宠物店线上平台:从 0 到 1 的全栈开发实践
  • 2024-2025华为ICT大赛中国区 实践赛网络赛道(高教组)全国总决赛 理论部分真题+解析
  • KNN算法实现图片的识别
  • 实战演练1:实战演练之命名实体识别
  • JavaScript数组去重性能优化:Set与Object哈希表为何效率最高
  • 俄罗斯方块游戏开发(面向对象编程)
  • 通过v4l2,采集视频,FFmpeg编码压缩封装视频(三)
  • Python-初学openCV——图像预处理(三)
  • 01人工智能中优雅草商业实战项目视频字幕翻译以及声音转译之底层处理逻辑阐述-卓伊凡|莉莉
  • Python 数据分析(四):Pandas 进阶
  • macOS配置 GO语言环境
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 微博舆情分析实现
  • Linux 系统网络配置及 IP 地址相关知识汇总
  • 八股文Kafka学习
  • 哈希表应用(map,set共同作用)
  • 基于 KNN 算法的手写数字识别项目实践
  • DAY21-二叉树的遍历方式