using Dpz.Core.Service.Mediator.Features.Account.Internal;
using Dpz.Core.Service.Mediator.Features.Account.Queries;
namespace Dpz.Core.Service.Mediator.Features.Account.Commands;
/// <summary>
/// 处理修改账号资料请求
/// </summary>
public class UpdateAccountInfoHandler(
IRepository<User> userRepository,
IRepository<UserHistory> userHistoryRepository,
IRepository<UserExtendedInfo> userExtendedInfoRepository,
IMediator mediator,
IFusionCache fusionCache
) : IRequestHandler<UpdateAccountInfoRequest, ResponseResult<VmUserInfo?>>
{
/// <summary>
/// 修改账号资料并返回最新用户信息
/// </summary>
public async ValueTask<ResponseResult<VmUserInfo?>> Handle(
UpdateAccountInfoRequest request,
CancellationToken cancellationToken
)
{
var result = new ResponseResult<VmUserInfo?>();
var user = await mediator.Send(
new GetUserInfoRequest { Account = request.Account },
cancellationToken
);
if (user == null)
{
return result.WithFail($"账号【{request.Account}】不存在");
}
var changes = new List<ChangeDetail>();
if (user.Name != request.Name)
{
changes.Add(AccountRepositoryHelper.Change(nameof(user.Name), user.Name, request.Name));
}
if (user.Sign != request.Sign)
{
changes.Add(AccountRepositoryHelper.Change(nameof(user.Sign), user.Sign, request.Sign));
}
if (user.Sex != request.Sex)
{
changes.Add(AccountRepositoryHelper.Change(nameof(user.Sex), user.Sex, request.Sex));
}
if (user.Email != request.Email)
{
if (!string.IsNullOrWhiteSpace(request.Email))
{
var exists = await userExtendedInfoRepository
.SearchFor(x => x.Email == request.Email && x.Id != user.Id)
.AnyAsync(cancellationToken: cancellationToken);
if (exists)
{
return result.WithFail($"邮箱【{request.Email}】已被使用");
}
}
changes.Add(
AccountRepositoryHelper.Change(nameof(user.Email), user.Email, request.Email)
);
}
if (changes.Count == 0)
{
return result.WithOk(user);
}
var nameExists = await userRepository
.SearchFor(x => x.Name == request.Name && x.Id != user.Id)
.AnyAsync(cancellationToken: cancellationToken);
if (nameExists)
{
return result.WithFail($"昵称【{request.Name}】已被使用");
}
await AccountRepositoryHelper.SaveUserHistoryAsync(
userHistoryRepository,
user.Id,
changes,
cancellationToken
);
var update = Builders<User>
.Update.Set(x => x.Name, request.Name)
.Set(x => x.Sign, request.Sign)
.Set(x => x.Sex, request.Sex)
.Set(x => x.LastUpdateTime, DateTime.Now);
await userRepository.UpdateAsync(x => x.Id == user.Id, update, cancellationToken);
var userExtendedInfo = await userExtendedInfoRepository
.SearchFor(x => x.Id == user.Id)
.SingleOrDefaultAsync(cancellationToken: cancellationToken);
if (userExtendedInfo == null)
{
await userExtendedInfoRepository.InsertAsync(
new UserExtendedInfo { Id = user.Id, Email = request.Email },
cancellationToken
);
}
else
{
var userExtendedInfoUpdate = Builders<UserExtendedInfo>
.Update.Set(x => x.Email, request.Email)
.Set(x => x.LastUpdateTime, DateTime.Now);
await userExtendedInfoRepository.UpdateAsync(
x => x.Id == user.Id,
userExtendedInfoUpdate,
cancellationToken
);
}
var userInfo = await mediator.Send(
new GetUserInfoRequest { Account = request.Account },
cancellationToken
);
await fusionCache.SetAsync(
UserSessionKeys.ChangedAtKey(request.Account),
DateTime.Now,
TimeSpan.FromDays(30),
token: cancellationToken
);
return result.WithOk(userInfo);
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
代码解释
这是一个基于 CQRS + Mediator 模式的账号信息更新处理器(Handler),用于处理用户资料修改请求。
主要功能
处理 UpdateAccountInfoRequest 请求,更新用户的基本信息(昵称、签名、性别、邮箱),并返回更新后的用户信息。
核心流程
1. 依赖注入
通过主构造函数注入以下依赖:
IRepository<User>: 用户仓储IRepository<UserHistory>: 用户历史记录仓储IRepository<UserExtendedInfo>: 用户扩展信息仓储IMediator: 中介者,用于发送查询请求IFusionCache: 融合缓存,用于缓存更新标记
2. 验证用户存在性
var user = await mediator.Send(new GetUserInfoRequest { Account = request.Account }, ...);
通过 Mediator 发送查询请求获取用户信息,若不存在则返回失败。
3. 变更检测
比对新旧值,记录发生变化的字段:
- Name(昵称)
- Sign(签名)
- Sex(性别)
- Email(邮箱)
使用 AccountRepositoryHelper.Change() 创建变更详情对象。
4. 邮箱唯一性校验
var exists = await userExtendedInfoRepository
.SearchFor(x => x.Email == request.Email && x.Id != user.Id)
.AnyAsync(...);
检查新邮箱是否已被其他用户使用。
5. 无变更直接返回
if (changes.Count == 0)
return result.WithOk(user);
若没有任何字段变化,直接返回当前用户信息。
6. 昵称唯一性校验
var nameExists = await userRepository
.SearchFor(x => x.Name == request.Name && x.Id != user.Id)
.AnyAsync(...);
确保昵称在系统中唯一。
7. 保存变更历史
await AccountRepositoryHelper.SaveUserHistoryAsync(
userHistoryRepository, user.Id, changes, cancellationToken
);
将变更记录写入 UserHistory 表,用于审计追踪。
8. 更新用户基本信息
使用 MongoDB 的 Builders<User>.Update 构建更新语句:
- 更新 Name、Sign、Sex
- 更新 LastUpdateTime 为当前时间
9. 更新/插入扩展信息
- 若
UserExtendedInfo不存在,则插入新记录 - 若存在,则更新 Email 和 LastUpdateTime
10. 更新缓存并返回
await fusionCache.SetAsync(
UserSessionKeys.ChangedAtKey(request.Account),
DateTime.Now,
TimeSpan.FromDays(30),
...
);
- 在缓存中记录账号的最后修改时间(30天有效期)
- 重新查询最新用户信息并返回
技术特点
| 特点 | 说明 |
|---|---|
| CQRS | 命令查询职责分离,通过 Mediator 发送查询请求 |
| 审计日志 | 记录字段变更历史到 UserHistory |
| 数据完整性 | 邮箱、昵称唯一性校验 |
| 缓存失效 | 更新后刷新缓存的修改时间戳 |
| 原子操作 | 使用仓储模式统一管理数据库操作 |
潜在改进建议
- 事务处理:多表更新应考虑使用分布式事务或 UnitOfWork 模式
- 异常处理:缺少数据库操作失败的显式处理
- 验证抽取:邮箱格式、昵称长度等验证可提前到请求验证层
- 性能优化:两次查询用户信息可合并为一次
AI 正在分析代码…
评论加载中...