146 lines
6.3 KiB
C#
146 lines
6.3 KiB
C#
using MediatR;
|
|
using TakeoutSaaS.Application.App.Personal.Dto;
|
|
using TakeoutSaaS.Application.App.Personal.Queries;
|
|
using TakeoutSaaS.Application.App.Personal.Services;
|
|
using TakeoutSaaS.Domain.Identity.Enums;
|
|
using TakeoutSaaS.Domain.Identity.Repositories;
|
|
using TakeoutSaaS.Domain.Merchants.Repositories;
|
|
using TakeoutSaaS.Domain.Tenants.Repositories;
|
|
using TakeoutSaaS.Shared.Abstractions.Constants;
|
|
using TakeoutSaaS.Shared.Abstractions.Exceptions;
|
|
|
|
namespace TakeoutSaaS.Application.App.Personal.Handlers;
|
|
|
|
/// <summary>
|
|
/// 获取个人中心总览处理器。
|
|
/// </summary>
|
|
public sealed class GetPersonalOverviewQueryHandler(
|
|
PersonalContextService personalContextService,
|
|
PersonalMaskingService personalMaskingService,
|
|
PersonalModuleStatusService moduleStatusService,
|
|
IIdentityUserRepository identityUserRepository,
|
|
ITenantRepository tenantRepository,
|
|
ITenantPackageRepository tenantPackageRepository,
|
|
IMerchantRepository merchantRepository,
|
|
IMediator mediator)
|
|
: IRequestHandler<GetPersonalOverviewQuery, PersonalOverviewDto>
|
|
{
|
|
/// <summary>
|
|
/// 处理查询。
|
|
/// </summary>
|
|
/// <param name="request">查询请求。</param>
|
|
/// <param name="cancellationToken">取消标记。</param>
|
|
/// <returns>总览结果。</returns>
|
|
public async Task<PersonalOverviewDto> Handle(GetPersonalOverviewQuery request, CancellationToken cancellationToken)
|
|
{
|
|
// 1. 获取必需上下文
|
|
var context = personalContextService.GetRequiredContext();
|
|
var traceId = context.TraceId;
|
|
|
|
// 2. 初始化总览容器
|
|
var moduleStatuses = new List<PersonalModuleStatusDto>();
|
|
PersonalAccountProfileDto? accountProfile = null;
|
|
PersonalSecuritySnapshotDto? securitySnapshot = null;
|
|
PersonalRolePermissionSummaryDto? roleSummary = null;
|
|
PersonalTenantAffiliationDto? tenantAffiliation = null;
|
|
|
|
// 3. 加载账号与安全模块
|
|
var user = await identityUserRepository.FindByIdAsync(context.UserId, cancellationToken)
|
|
?? throw new BusinessException(ErrorCodes.NotFound, "用户不存在");
|
|
if (user.TenantId != context.TenantId)
|
|
{
|
|
throw new BusinessException(ErrorCodes.Forbidden, "无权访问其他租户用户数据");
|
|
}
|
|
|
|
accountProfile = new PersonalAccountProfileDto
|
|
{
|
|
UserId = user.Id,
|
|
Account = user.Account,
|
|
DisplayName = user.DisplayName,
|
|
AvatarUrl = user.Avatar,
|
|
PhoneMasked = personalMaskingService.MaskPhone(user.Phone),
|
|
EmailMasked = personalMaskingService.MaskEmail(user.Email),
|
|
RegisteredAt = user.CreatedAt
|
|
};
|
|
securitySnapshot = new PersonalSecuritySnapshotDto
|
|
{
|
|
LastLoginAt = user.LastLoginAt,
|
|
FailedLoginCount = user.FailedLoginCount,
|
|
IsLocked = user.Status == IdentityUserStatus.Locked || (user.LockedUntil.HasValue && user.LockedUntil > DateTime.UtcNow),
|
|
LockedUntil = user.LockedUntil,
|
|
IsForceChangePassword = user.MustChangePassword
|
|
};
|
|
moduleStatuses.Add(moduleStatusService.BuildOk("accountSecurity", traceId));
|
|
|
|
// 4. 加载角色权限模块(失败可降级)
|
|
try
|
|
{
|
|
roleSummary = await mediator.Send(new GetPersonalRolesQuery(), cancellationToken);
|
|
moduleStatuses.Add(moduleStatusService.BuildOk("roleSummary", traceId));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
moduleStatuses.Add(moduleStatusService.BuildIssue("roleSummary", "degraded", ErrorCodes.InternalServerError.ToString(), ex.Message, traceId));
|
|
}
|
|
|
|
// 5. 加载租户归属模块(失败可降级)
|
|
try
|
|
{
|
|
var tenant = await tenantRepository.FindByIdAsync(context.TenantId, cancellationToken)
|
|
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
|
var subscription = await tenantRepository.GetActiveSubscriptionAsync(context.TenantId, cancellationToken);
|
|
|
|
string? packageName = null;
|
|
if (subscription is not null)
|
|
{
|
|
var package = await tenantPackageRepository.FindByIdAsync(subscription.TenantPackageId, cancellationToken);
|
|
packageName = package?.Name;
|
|
}
|
|
|
|
string? merchantName = null;
|
|
string merchantStatus = "unknown";
|
|
if (user.MerchantId is > 0)
|
|
{
|
|
var merchant = await merchantRepository.FindByIdAsync(user.MerchantId.Value, context.TenantId, cancellationToken);
|
|
merchantName = merchant?.BrandName;
|
|
merchantStatus = merchant?.Status.ToString().ToLowerInvariant() ?? "unknown";
|
|
}
|
|
|
|
tenantAffiliation = new PersonalTenantAffiliationDto
|
|
{
|
|
TenantId = tenant.Id,
|
|
TenantName = tenant.Name,
|
|
MerchantId = user.MerchantId,
|
|
MerchantName = merchantName,
|
|
MerchantStatus = merchantStatus,
|
|
PackageName = packageName,
|
|
SubscriptionExpireAt = subscription?.EffectiveTo ?? tenant.EffectiveTo
|
|
};
|
|
moduleStatuses.Add(moduleStatusService.BuildOk("tenantAffiliation", traceId));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
moduleStatuses.Add(moduleStatusService.BuildIssue("tenantAffiliation", "degraded", ErrorCodes.InternalServerError.ToString(), ex.Message, traceId));
|
|
}
|
|
|
|
// 6. 计算总体状态并返回
|
|
var hasAnyData = accountProfile is not null
|
|
|| securitySnapshot is not null
|
|
|| roleSummary is not null
|
|
|| tenantAffiliation is not null;
|
|
var requestId = string.IsNullOrWhiteSpace(traceId) ? Guid.NewGuid().ToString("N") : traceId;
|
|
var overallStatus = moduleStatusService.ResolveOverallStatus(moduleStatuses, hasAnyData);
|
|
return new PersonalOverviewDto
|
|
{
|
|
RequestId = requestId,
|
|
OverallStatus = overallStatus,
|
|
AccountProfile = accountProfile,
|
|
SecuritySnapshot = securitySnapshot,
|
|
RoleSummary = roleSummary,
|
|
TenantAffiliation = tenantAffiliation,
|
|
QuotaSummary = null,
|
|
ModuleStatuses = moduleStatuses
|
|
};
|
|
}
|
|
}
|