当前位置: 首页 > 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.lryc.cn/news/601559.html

相关文章:

  • GitHub 趋势日报 (2025年07月25日)
  • 9.SpringBoot Web请求参数绑定方法
  • 设计模式(九)结构型:组合模式详解
  • 设计模式(四)创建型:生成器模式详解
  • 第四科学范式(数据密集型科学):科学发现的新范式
  • NLP学习开始01-线性回归
  • 多租户Kubernetes集群架构设计实践——隔离、安全与弹性扩缩容
  • Vue基础(25)_组件与Vue的内置关系(原型链)
  • 马尔可夫链
  • MYSQL-- 行锁在索引命中与覆盖情况下的加锁行为
  • 随机密码生成
  • RTSP|RTMP播放器 in Unity:开源不够用?从工程视角重新定义播放器选型
  • Tkinter美化 - 告别土味Python GUI
  • 设计模式(二)创建型:工厂方法模式详解
  • 哈希表应用(map,set共同作用)
  • ubuntu18.04解压大的tar.gz文件失败
  • MySQL 全详解:从入门到精通的实战指南
  • vulhub-red靶机攻略
  • 优化Linux高并发:文件描述符与端口范围的协同调优
  • 【橘子分布式】gRPC(番外篇-客户端重试机制)
  • Python爬虫实战:研究tldextract库相关技术构建新闻网站域名分析爬虫系统
  • Java学习-------桥接模式
  • 3D芯片香港集成:技术突破与产业机遇全景分析
  • Python操作Excel文件完整指南
  • 依赖倒置原则 Dependency Inversion Principle - DIP
  • 2025 环法对决,VELO Angel Glide 坐垫轻装上阵
  • python优秀案例:基于python flask实现的小说文本数据分析与挖掘系统,包括K-means聚类算法和LDA主题分析
  • HBuilder X打包发布微信小程序
  • rust-包和箱子
  • 主要分布于内侧内嗅皮层的层Ⅲ的边界向量细胞(BVCs)对NLP中的深层语义分析的积极影响和启示