fix:修复注释错误
This commit is contained in:
@@ -63,7 +63,7 @@ public sealed class CheckTenantQuotaCommandHandler(
|
||||
ResetCycle = ResolveResetCycle(request.QuotaType)
|
||||
};
|
||||
|
||||
// 4.1 (空行后) 记录是否为首次初始化(用于落库历史)
|
||||
// 4.1 记录是否为首次初始化(用于落库历史)
|
||||
var isNewUsage = usage.Id == 0;
|
||||
|
||||
var usedAfter = usage.UsedValue + request.Delta;
|
||||
@@ -79,7 +79,7 @@ public sealed class CheckTenantQuotaCommandHandler(
|
||||
usage.UsedValue = usedAfter;
|
||||
usage.ResetCycle ??= ResolveResetCycle(request.QuotaType);
|
||||
|
||||
// 5.1 (空行后) 落库历史(初始化 + 本次消耗)
|
||||
// 5.1 落库历史(初始化 + 本次消耗)
|
||||
var now = DateTime.UtcNow;
|
||||
if (isNewUsage)
|
||||
{
|
||||
|
||||
@@ -39,13 +39,13 @@ public sealed class ClaimTenantReviewCommandHandler(
|
||||
throw new BusinessException(ErrorCodes.Conflict, $"该审核已被 {existingClaim.ClaimedByName} 领取");
|
||||
}
|
||||
|
||||
// 3. (空行后) 获取当前用户显示名(用于展示快照)
|
||||
// 3. 获取当前用户显示名(用于展示快照)
|
||||
var profile = await adminAuthService.GetProfileAsync(currentUserAccessor.UserId, cancellationToken);
|
||||
var displayName = string.IsNullOrWhiteSpace(profile.DisplayName)
|
||||
? $"user:{currentUserAccessor.UserId}"
|
||||
: profile.DisplayName;
|
||||
|
||||
// 4. (空行后) 构造领取记录与审计日志
|
||||
// 4. 构造领取记录与审计日志
|
||||
var now = DateTime.UtcNow;
|
||||
var claim = new TenantReviewClaim
|
||||
{
|
||||
@@ -68,7 +68,7 @@ public sealed class ClaimTenantReviewCommandHandler(
|
||||
CurrentStatus = tenant.Status
|
||||
};
|
||||
|
||||
// 5. (空行后) 写入领取记录(处理并发领取冲突)
|
||||
// 5. 写入领取记录(处理并发领取冲突)
|
||||
var success = await tenantRepository.TryAddReviewClaimAsync(claim, auditLog, cancellationToken);
|
||||
if (!success)
|
||||
{
|
||||
@@ -86,7 +86,7 @@ public sealed class ClaimTenantReviewCommandHandler(
|
||||
throw new BusinessException(ErrorCodes.Conflict, $"该审核已被 {current.ClaimedByName} 领取");
|
||||
}
|
||||
|
||||
// 6. (空行后) 返回领取结果
|
||||
// 6. 返回领取结果
|
||||
return claim.ToDto();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,11 +37,11 @@ public sealed class CreateTenantAdminResetLinkTokenCommandHandler(
|
||||
throw new BusinessException(ErrorCodes.Forbidden, "仅平台超级管理员可生成重置链接");
|
||||
}
|
||||
|
||||
// 2. (空行后) 校验租户存在且存在主管理员
|
||||
// 2. 校验租户存在且存在主管理员
|
||||
var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
||||
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
||||
|
||||
// 2.1 (空行后) 若缺少主管理员则自动回填(兼容历史数据)
|
||||
// 2.1 若缺少主管理员则自动回填(兼容历史数据)
|
||||
if (!tenant.PrimaryOwnerUserId.HasValue || tenant.PrimaryOwnerUserId.Value == 0)
|
||||
{
|
||||
var originalContextForFix = tenantContextAccessor.Current;
|
||||
@@ -65,10 +65,10 @@ public sealed class CreateTenantAdminResetLinkTokenCommandHandler(
|
||||
}
|
||||
}
|
||||
|
||||
// 3. (空行后) 签发一次性重置令牌(默认 24 小时有效)
|
||||
// 3. 签发一次性重置令牌(默认 24 小时有效)
|
||||
var token = await tokenStore.IssueAsync(tenant.PrimaryOwnerUserId.Value, DateTime.UtcNow.AddHours(24), cancellationToken);
|
||||
|
||||
// 4. (空行后) 写入审计日志
|
||||
// 4. 写入审计日志
|
||||
var operatorProfile = await adminAuthService.GetProfileAsync(currentUserAccessor.UserId, cancellationToken);
|
||||
var operatorName = string.IsNullOrWhiteSpace(operatorProfile.DisplayName)
|
||||
? $"user:{currentUserAccessor.UserId}"
|
||||
@@ -88,7 +88,7 @@ public sealed class CreateTenantAdminResetLinkTokenCommandHandler(
|
||||
await tenantRepository.AddAuditLogAsync(auditLog, cancellationToken);
|
||||
await tenantRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 5. (空行后) 返回令牌
|
||||
// 5. 返回令牌
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public sealed class CreateTenantManuallyCommandHandler(
|
||||
throw new BusinessException(ErrorCodes.Conflict, $"租户编码 {normalizedCode} 已存在");
|
||||
}
|
||||
|
||||
// 3. (空行后) 校验联系人手机号唯一性(仅当填写时)
|
||||
// 3. 校验联系人手机号唯一性(仅当填写时)
|
||||
if (!string.IsNullOrWhiteSpace(request.ContactPhone))
|
||||
{
|
||||
var normalizedPhone = request.ContactPhone.Trim();
|
||||
@@ -59,14 +59,14 @@ public sealed class CreateTenantManuallyCommandHandler(
|
||||
}
|
||||
}
|
||||
|
||||
// 4. (空行后) 校验管理员账号唯一性
|
||||
// 4. 校验管理员账号唯一性
|
||||
var normalizedAccount = request.AdminAccount.Trim();
|
||||
if (await identityUserRepository.ExistsByAccountAsync(normalizedAccount, cancellationToken))
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.Conflict, $"账号 {normalizedAccount} 已存在");
|
||||
}
|
||||
|
||||
// 5. (空行后) 校验套餐存在且可用
|
||||
// 5. 校验套餐存在且可用
|
||||
var package = await tenantPackageRepository.FindByIdAsync(request.TenantPackageId, cancellationToken)
|
||||
?? throw new BusinessException(ErrorCodes.NotFound, "套餐不存在");
|
||||
if (!package.IsActive)
|
||||
@@ -74,12 +74,12 @@ public sealed class CreateTenantManuallyCommandHandler(
|
||||
throw new BusinessException(ErrorCodes.BadRequest, "套餐未启用,无法绑定订阅");
|
||||
}
|
||||
|
||||
// 6. (空行后) 计算订阅生效与到期时间(UTC)
|
||||
// 6. 计算订阅生效与到期时间(UTC)
|
||||
var now = DateTime.UtcNow;
|
||||
var subscriptionEffectiveFrom = request.SubscriptionEffectiveFrom ?? now;
|
||||
var subscriptionEffectiveTo = subscriptionEffectiveFrom.AddMonths(request.DurationMonths);
|
||||
|
||||
// 7. (空行后) 构建租户与订阅
|
||||
// 7. 构建租户与订阅
|
||||
var tenantId = idGenerator.NextId();
|
||||
var tenant = new Tenant
|
||||
{
|
||||
@@ -108,7 +108,7 @@ public sealed class CreateTenantManuallyCommandHandler(
|
||||
Remarks = request.Remarks
|
||||
};
|
||||
|
||||
// 8. (空行后) 构建订阅实体
|
||||
// 8. 构建订阅实体
|
||||
var subscription = new TenantSubscription
|
||||
{
|
||||
Id = idGenerator.NextId(),
|
||||
@@ -123,7 +123,7 @@ public sealed class CreateTenantManuallyCommandHandler(
|
||||
Notes = request.SubscriptionNotes
|
||||
};
|
||||
|
||||
// 9. (空行后) 构建认证资料(默认直接通过)
|
||||
// 9. 构建认证资料(默认直接通过)
|
||||
var actorName = currentUserAccessor.IsAuthenticated
|
||||
? $"user:{currentUserAccessor.UserId}"
|
||||
: "system";
|
||||
@@ -150,7 +150,7 @@ public sealed class CreateTenantManuallyCommandHandler(
|
||||
ReviewRemarks = request.ReviewRemarks
|
||||
};
|
||||
|
||||
// 10. (空行后) 写入审计日志与订阅历史
|
||||
// 10. 写入审计日志与订阅历史
|
||||
await tenantRepository.AddAuditLogAsync(new TenantAuditLog
|
||||
{
|
||||
TenantId = tenantId,
|
||||
@@ -201,13 +201,13 @@ public sealed class CreateTenantManuallyCommandHandler(
|
||||
Notes = request.SubscriptionNotes
|
||||
}, cancellationToken);
|
||||
|
||||
// 11. (空行后) 持久化租户、订阅与认证资料
|
||||
// 11. 持久化租户、订阅与认证资料
|
||||
await tenantRepository.AddTenantAsync(tenant, cancellationToken);
|
||||
await tenantRepository.AddSubscriptionAsync(subscription, cancellationToken);
|
||||
await tenantRepository.UpsertVerificationProfileAsync(verification, cancellationToken);
|
||||
await tenantRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 12. (空行后) 临时切换租户上下文,保证身份与权限写入正确
|
||||
// 12. 临时切换租户上下文,保证身份与权限写入正确
|
||||
var previousContext = tenantContextAccessor.Current;
|
||||
tenantContextAccessor.Current = new TenantContext(tenant.Id, tenant.Code, "manual-create");
|
||||
try
|
||||
@@ -228,7 +228,7 @@ public sealed class CreateTenantManuallyCommandHandler(
|
||||
await identityUserRepository.AddAsync(adminUser, cancellationToken);
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 14. (空行后) 初始化租户管理员角色模板并绑定角色
|
||||
// 14. 初始化租户管理员角色模板并绑定角色
|
||||
await mediator.Send(new InitializeRoleTemplatesCommand
|
||||
{
|
||||
TemplateCodes = new[] { "tenant-admin" }
|
||||
@@ -244,7 +244,7 @@ public sealed class CreateTenantManuallyCommandHandler(
|
||||
}, cancellationToken);
|
||||
}
|
||||
|
||||
// 15. (空行后) 回写租户所有者账号
|
||||
// 15. 回写租户所有者账号
|
||||
tenant.PrimaryOwnerUserId = adminUser.Id;
|
||||
await tenantRepository.UpdateTenantAsync(tenant, cancellationToken);
|
||||
await tenantRepository.SaveChangesAsync(cancellationToken);
|
||||
@@ -255,7 +255,7 @@ public sealed class CreateTenantManuallyCommandHandler(
|
||||
tenantContextAccessor.Current = previousContext;
|
||||
}
|
||||
|
||||
// 17. (空行后) 返回创建结果
|
||||
// 17. 返回创建结果
|
||||
logger.LogInformation("已后台手动创建租户 {TenantCode}", tenant.Code);
|
||||
|
||||
return new TenantDetailDto
|
||||
|
||||
@@ -45,7 +45,7 @@ public sealed class ExtendTenantSubscriptionCommandHandler(
|
||||
? $"user:{currentUserAccessor.UserId}"
|
||||
: "system";
|
||||
|
||||
// 3. (空行后) 创建续费订阅
|
||||
// 3. 创建续费订阅
|
||||
var subscription = new TenantSubscription
|
||||
{
|
||||
Id = idGenerator.NextId(),
|
||||
@@ -75,7 +75,7 @@ public sealed class ExtendTenantSubscriptionCommandHandler(
|
||||
Notes = request.Notes
|
||||
}, cancellationToken);
|
||||
|
||||
// 4. (空行后) 若租户处于到期状态则恢复为正常(冻结状态需先解冻)
|
||||
// 4. 若租户处于到期状态则恢复为正常(冻结状态需先解冻)
|
||||
if (tenant.Status == TenantStatus.Expired)
|
||||
{
|
||||
tenant.Status = TenantStatus.Active;
|
||||
@@ -86,7 +86,7 @@ public sealed class ExtendTenantSubscriptionCommandHandler(
|
||||
|
||||
await tenantRepository.UpdateTenantAsync(tenant, cancellationToken);
|
||||
|
||||
// 5. (空行后) 记录审计
|
||||
// 5. 记录审计
|
||||
await tenantRepository.AddAuditLogAsync(new TenantAuditLog
|
||||
{
|
||||
TenantId = tenant.Id,
|
||||
|
||||
@@ -33,7 +33,7 @@ public sealed class ForceClaimTenantReviewCommandHandler(
|
||||
? $"user:{currentUserAccessor.UserId}"
|
||||
: profile.DisplayName;
|
||||
|
||||
// 3. (空行后) 读取当前领取记录(可跟踪用于更新)
|
||||
// 3. 读取当前领取记录(可跟踪用于更新)
|
||||
var claim = await tenantRepository.FindActiveReviewClaimAsync(request.TenantId, cancellationToken);
|
||||
if (claim == null)
|
||||
{
|
||||
@@ -75,13 +75,13 @@ public sealed class ForceClaimTenantReviewCommandHandler(
|
||||
return created.ToDto();
|
||||
}
|
||||
|
||||
// 5. (空行后) 已由自己领取则直接返回
|
||||
// 5. 已由自己领取则直接返回
|
||||
if (claim.ClaimedBy == currentUserAccessor.UserId)
|
||||
{
|
||||
return claim.ToDto();
|
||||
}
|
||||
|
||||
// 6. (空行后) 更新领取人并记录审计
|
||||
// 6. 更新领取人并记录审计
|
||||
var previousOwner = claim.ClaimedByName;
|
||||
claim.ClaimedBy = currentUserAccessor.UserId;
|
||||
claim.ClaimedByName = displayName;
|
||||
|
||||
@@ -44,7 +44,7 @@ public sealed class FreezeTenantCommandHandler(
|
||||
tenant.SuspendedAt = DateTime.UtcNow;
|
||||
tenant.SuspensionReason = request.Reason;
|
||||
|
||||
// 3. (空行后) 同步暂停订阅
|
||||
// 3. 同步暂停订阅
|
||||
if (subscription != null)
|
||||
{
|
||||
subscription.Status = SubscriptionStatus.Suspended;
|
||||
@@ -53,7 +53,7 @@ public sealed class FreezeTenantCommandHandler(
|
||||
|
||||
await tenantRepository.UpdateTenantAsync(tenant, cancellationToken);
|
||||
|
||||
// 4. (空行后) 记录审计
|
||||
// 4. 记录审计
|
||||
var actorName = currentUserAccessor.IsAuthenticated
|
||||
? $"user:{currentUserAccessor.UserId}"
|
||||
: "system";
|
||||
|
||||
@@ -26,12 +26,12 @@ public sealed class GetTenantByIdQueryHandler(
|
||||
var subscription = await tenantRepository.GetActiveSubscriptionAsync(request.TenantId, cancellationToken);
|
||||
var verification = await tenantRepository.GetVerificationProfileAsync(request.TenantId, cancellationToken);
|
||||
|
||||
// 3. (空行后) 查询当前套餐
|
||||
// 3. 查询当前套餐
|
||||
var package = subscription == null
|
||||
? null
|
||||
: await tenantPackageRepository.FindByIdAsync(subscription.TenantPackageId, cancellationToken);
|
||||
|
||||
// 4. (空行后) 组装返回
|
||||
// 4. 组装返回
|
||||
return new TenantDetailDto
|
||||
{
|
||||
Tenant = TenantMapping.ToDto(tenant, subscription, verification),
|
||||
|
||||
@@ -24,13 +24,13 @@ public sealed class GetTenantPackageTenantsQueryHandler(IDapperExecutor dapperEx
|
||||
var pageSize = request.PageSize <= 0 ? 20 : request.PageSize;
|
||||
var keyword = string.IsNullOrWhiteSpace(request.Keyword) ? null : request.Keyword.Trim();
|
||||
|
||||
// 2. (空行后) 以当前时间为准筛选“有效订阅”
|
||||
// 2. 以当前时间为准筛选“有效订阅”
|
||||
var now = DateTime.UtcNow;
|
||||
var expiringDays = request.ExpiringWithinDays is > 0 ? request.ExpiringWithinDays : null;
|
||||
var expiryEnd = expiringDays.HasValue ? now.AddDays(expiringDays.Value) : (DateTime?)null;
|
||||
var offset = (page - 1) * pageSize;
|
||||
|
||||
// 3. (空行后) 查询总数 + 列表
|
||||
// 3. 查询总数 + 列表
|
||||
return await dapperExecutor.QueryAsync(
|
||||
DatabaseConstants.AppDataSource,
|
||||
DatabaseConnectionRole.Read,
|
||||
@@ -48,7 +48,7 @@ public sealed class GetTenantPackageTenantsQueryHandler(IDapperExecutor dapperEx
|
||||
],
|
||||
token);
|
||||
|
||||
// 3.2 (空行后) 查询列表
|
||||
// 3.2 查询列表
|
||||
var listSql = BuildListSql(expiryEnd.HasValue);
|
||||
await using var listCommand = CreateCommand(
|
||||
connection,
|
||||
@@ -79,7 +79,7 @@ public sealed class GetTenantPackageTenantsQueryHandler(IDapperExecutor dapperEx
|
||||
});
|
||||
}
|
||||
|
||||
// 3.3 (空行后) 返回分页
|
||||
// 3.3 返回分页
|
||||
return new PagedResult<TenantPackageTenantDto>(items, page, pageSize, total);
|
||||
},
|
||||
cancellationToken);
|
||||
|
||||
@@ -23,14 +23,14 @@ public sealed class GetTenantPackageUsagesQueryHandler(IDapperExecutor dapperExe
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
|
||||
// 2. (空行后) 构造 SQL(以当前时间为准统计“有效订阅/使用租户/到期分布”)
|
||||
// 2. 构造 SQL(以当前时间为准统计“有效订阅/使用租户/到期分布”)
|
||||
var now = DateTime.UtcNow;
|
||||
var date7 = now.AddDays(7);
|
||||
var date15 = now.AddDays(15);
|
||||
var date30 = now.AddDays(30);
|
||||
var sql = BuildSql(ids, out var parameters, now, date7, date15, date30);
|
||||
|
||||
// 3. (空行后) 查询统计结果
|
||||
// 3. 查询统计结果
|
||||
return await dapperExecutor.QueryAsync(
|
||||
DatabaseConstants.AppDataSource,
|
||||
DatabaseConnectionRole.Read,
|
||||
@@ -40,7 +40,7 @@ public sealed class GetTenantPackageUsagesQueryHandler(IDapperExecutor dapperExe
|
||||
await using var reader = await command.ExecuteReaderAsync(token);
|
||||
var list = new List<TenantPackageUsageDto>();
|
||||
|
||||
// 4. (空行后) 逐行读取
|
||||
// 4. 逐行读取
|
||||
while (await reader.ReadAsync(token))
|
||||
{
|
||||
list.Add(new TenantPackageUsageDto
|
||||
@@ -94,7 +94,7 @@ public sealed class GetTenantPackageUsagesQueryHandler(IDapperExecutor dapperExe
|
||||
("date30", date30)
|
||||
};
|
||||
|
||||
// 2. (空行后) 可选按套餐 ID 过滤
|
||||
// 2. 可选按套餐 ID 过滤
|
||||
if (ids is { Length: > 0 })
|
||||
{
|
||||
builder.Append(" and \"TenantPackageId\" in (");
|
||||
@@ -113,7 +113,7 @@ public sealed class GetTenantPackageUsagesQueryHandler(IDapperExecutor dapperExe
|
||||
builder.AppendLine(")");
|
||||
}
|
||||
|
||||
// 3. (空行后) 分组与回连套餐表
|
||||
// 3. 分组与回连套餐表
|
||||
builder.AppendLine("""
|
||||
group by "TenantPackageId"
|
||||
)
|
||||
|
||||
@@ -27,12 +27,12 @@ public sealed class GetTenantQuotaUsageHistoryQueryHandler(
|
||||
_ = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
||||
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
||||
|
||||
// 2. (空行后) 规范化分页
|
||||
// 2. 规范化分页
|
||||
var page = request.Page <= 0 ? 1 : request.Page;
|
||||
var pageSize = request.PageSize is <= 0 or > 100 ? 10 : request.PageSize;
|
||||
var offset = (page - 1) * pageSize;
|
||||
|
||||
// 3. (空行后) 查询总数 + 列表
|
||||
// 3. 查询总数 + 列表
|
||||
return await dapperExecutor.QueryAsync(
|
||||
DatabaseConstants.AppDataSource,
|
||||
DatabaseConnectionRole.Read,
|
||||
@@ -50,7 +50,7 @@ public sealed class GetTenantQuotaUsageHistoryQueryHandler(
|
||||
],
|
||||
token);
|
||||
|
||||
// 3.2 (空行后) 查询列表
|
||||
// 3.2 查询列表
|
||||
await using var listCommand = CreateCommand(
|
||||
connection,
|
||||
BuildListSql(),
|
||||
@@ -75,7 +75,7 @@ public sealed class GetTenantQuotaUsageHistoryQueryHandler(
|
||||
decimal? changeAmount = reader.IsDBNull(5) ? null : reader.GetDecimal(5);
|
||||
var changeReason = reader.IsDBNull(6) ? null : reader.GetString(6);
|
||||
|
||||
// 3.2.1 (空行后) 映射 DTO
|
||||
// 3.2.1 映射 DTO
|
||||
items.Add(new QuotaUsageHistoryDto
|
||||
{
|
||||
QuotaType = quotaType,
|
||||
@@ -88,7 +88,7 @@ public sealed class GetTenantQuotaUsageHistoryQueryHandler(
|
||||
});
|
||||
}
|
||||
|
||||
// 3.3 (空行后) 返回分页
|
||||
// 3.3 返回分页
|
||||
return new PagedResult<QuotaUsageHistoryDto>(items, page, pageSize, total);
|
||||
},
|
||||
cancellationToken);
|
||||
|
||||
@@ -38,17 +38,17 @@ public sealed class ImpersonateTenantCommandHandler(
|
||||
throw new BusinessException(ErrorCodes.Forbidden, "仅平台超级管理员可执行伪装登录");
|
||||
}
|
||||
|
||||
// 2. (空行后) 读取操作者信息(在平台租户上下文内)
|
||||
// 2. 读取操作者信息(在平台租户上下文内)
|
||||
var operatorProfile = await adminAuthService.GetProfileAsync(currentUserAccessor.UserId, cancellationToken);
|
||||
var operatorName = string.IsNullOrWhiteSpace(operatorProfile.DisplayName)
|
||||
? $"user:{currentUserAccessor.UserId}"
|
||||
: operatorProfile.DisplayName;
|
||||
|
||||
// 2. (空行后) 校验租户存在且存在主管理员
|
||||
// 2. 校验租户存在且存在主管理员
|
||||
var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
||||
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
||||
|
||||
// 2.1 (空行后) 若缺少主管理员则自动回填(兼容历史数据)
|
||||
// 2.1 若缺少主管理员则自动回填(兼容历史数据)
|
||||
if (!tenant.PrimaryOwnerUserId.HasValue || tenant.PrimaryOwnerUserId.Value == 0)
|
||||
{
|
||||
var originalContextForFix = tenantContextAccessor.Current;
|
||||
@@ -72,7 +72,7 @@ public sealed class ImpersonateTenantCommandHandler(
|
||||
}
|
||||
}
|
||||
|
||||
// 3. (空行后) 进入目标租户上下文以读取租户内用户(避免多租户查询过滤导致找不到用户)
|
||||
// 3. 进入目标租户上下文以读取租户内用户(避免多租户查询过滤导致找不到用户)
|
||||
var originalTenantContext = tenantContextAccessor.Current;
|
||||
tenantContextAccessor.Current = new TenantContext(tenant.Id, null, "admin:impersonate");
|
||||
try
|
||||
@@ -81,7 +81,7 @@ public sealed class ImpersonateTenantCommandHandler(
|
||||
var targetProfile = await adminAuthService.GetProfileAsync(tenant.PrimaryOwnerUserId.Value, cancellationToken);
|
||||
var token = await jwtTokenService.CreateTokensAsync(targetProfile, false, cancellationToken);
|
||||
|
||||
// 5. (空行后) 恢复租户上下文后写入审计日志
|
||||
// 5. 恢复租户上下文后写入审计日志
|
||||
tenantContextAccessor.Current = originalTenantContext;
|
||||
var auditLog = new TenantAuditLog
|
||||
{
|
||||
@@ -97,12 +97,12 @@ public sealed class ImpersonateTenantCommandHandler(
|
||||
await tenantRepository.AddAuditLogAsync(auditLog, cancellationToken);
|
||||
await tenantRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 6. (空行后) 返回令牌
|
||||
// 6. 返回令牌
|
||||
return token;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 7. (空行后) 确保恢复租户上下文
|
||||
// 7. 确保恢复租户上下文
|
||||
tenantContextAccessor.Current = originalTenantContext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,13 +34,13 @@ public sealed class ReleaseTenantReviewClaimCommandHandler(
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. (空行后) 非领取人不允许释放(如需接管请使用强制接管)
|
||||
// 3. 非领取人不允许释放(如需接管请使用强制接管)
|
||||
if (claim.ClaimedBy != currentUserAccessor.UserId)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.Conflict, $"该审核已被 {claim.ClaimedByName} 领取");
|
||||
}
|
||||
|
||||
// 4. (空行后) 释放领取并记录审计
|
||||
// 4. 释放领取并记录审计
|
||||
var profile = await adminAuthService.GetProfileAsync(currentUserAccessor.UserId, cancellationToken);
|
||||
var displayName = string.IsNullOrWhiteSpace(profile.DisplayName)
|
||||
? $"user:{currentUserAccessor.UserId}"
|
||||
|
||||
@@ -202,7 +202,7 @@ public sealed class ReviewTenantCommandHandler(
|
||||
CurrentStatus = tenant.Status
|
||||
}, cancellationToken);
|
||||
|
||||
// 7. (空行后) 审核完成自动释放领取
|
||||
// 7. 审核完成自动释放领取
|
||||
reviewClaim.ReleasedAt = DateTime.UtcNow;
|
||||
await tenantRepository.UpdateReviewClaimAsync(reviewClaim, cancellationToken);
|
||||
await tenantRepository.AddAuditLogAsync(new Domain.Tenants.Entities.TenantAuditLog
|
||||
|
||||
@@ -27,24 +27,24 @@ public sealed class SearchTenantsQueryHandler(ITenantRepository tenantRepository
|
||||
request.PageSize,
|
||||
cancellationToken);
|
||||
|
||||
// 2. (空行后) 无数据直接返回
|
||||
// 2. 无数据直接返回
|
||||
if (tenants.Count == 0)
|
||||
{
|
||||
return new PagedResult<TenantDto>([], request.Page, request.PageSize, total);
|
||||
}
|
||||
|
||||
// 3. (空行后) 批量查询订阅与实名资料(避免 N+1)
|
||||
// 3. 批量查询订阅与实名资料(避免 N+1)
|
||||
var tenantIds = tenants.Select(x => x.Id).ToArray();
|
||||
var subscriptions = await tenantRepository.GetSubscriptionsAsync(tenantIds, cancellationToken);
|
||||
var verifications = await tenantRepository.GetVerificationProfilesAsync(tenantIds, cancellationToken);
|
||||
|
||||
// 4. (空行后) 构建订阅与实名资料映射
|
||||
// 4. 构建订阅与实名资料映射
|
||||
var subscriptionByTenantId = subscriptions
|
||||
.GroupBy(x => x.TenantId)
|
||||
.ToDictionary(x => x.Key, x => x.FirstOrDefault());
|
||||
var verificationByTenantId = verifications.ToDictionary(x => x.TenantId);
|
||||
|
||||
// 5. (空行后) 映射 DTO(带订阅与认证)
|
||||
// 5. 映射 DTO(带订阅与认证)
|
||||
var result = new List<TenantDto>(tenants.Count);
|
||||
foreach (var tenant in tenants)
|
||||
{
|
||||
@@ -53,7 +53,7 @@ public sealed class SearchTenantsQueryHandler(ITenantRepository tenantRepository
|
||||
result.Add(TenantMapping.ToDto(tenant, subscription, verification));
|
||||
}
|
||||
|
||||
// 6. (空行后) 返回分页结果
|
||||
// 6. 返回分页结果
|
||||
return new PagedResult<TenantDto>(result, request.Page, request.PageSize, total);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ public sealed class SelfRegisterTenantCommandHandler(
|
||||
await identityUserRepository.AddAsync(adminUser, cancellationToken);
|
||||
await identityUserRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 7.1 (空行后) 回填主管理员标识,确保后续伪装登录/重置管理员等能力可用
|
||||
// 7.1 回填主管理员标识,确保后续伪装登录/重置管理员等能力可用
|
||||
tenant.PrimaryOwnerUserId = adminUser.Id;
|
||||
await tenantRepository.UpdateTenantAsync(tenant, cancellationToken);
|
||||
await tenantRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
@@ -42,7 +42,7 @@ public sealed class UnfreezeTenantCommandHandler(
|
||||
tenant.SuspendedAt = null;
|
||||
tenant.SuspensionReason = null;
|
||||
|
||||
// 3. (空行后) 同步订阅状态
|
||||
// 3. 同步订阅状态
|
||||
if (subscription != null)
|
||||
{
|
||||
subscription.Status = isExpired ? SubscriptionStatus.GracePeriod : SubscriptionStatus.Active;
|
||||
@@ -51,7 +51,7 @@ public sealed class UnfreezeTenantCommandHandler(
|
||||
|
||||
await tenantRepository.UpdateTenantAsync(tenant, cancellationToken);
|
||||
|
||||
// 4. (空行后) 记录审计
|
||||
// 4. 记录审计
|
||||
var actorName = currentUserAccessor.IsAuthenticated
|
||||
? $"user:{currentUserAccessor.UserId}"
|
||||
: "system";
|
||||
|
||||
@@ -29,18 +29,18 @@ public sealed class UpdateTenantCommandHandler(
|
||||
throw new BusinessException(ErrorCodes.BadRequest, "租户名称不能为空");
|
||||
}
|
||||
|
||||
// 2. (空行后) 查询租户
|
||||
// 2. 查询租户
|
||||
var tenant = await tenantRepository.FindByIdAsync(request.TenantId, cancellationToken)
|
||||
?? throw new BusinessException(ErrorCodes.NotFound, "租户不存在");
|
||||
|
||||
// 3. (空行后) 校验租户名称唯一性(排除自身)
|
||||
// 3. 校验租户名称唯一性(排除自身)
|
||||
var normalizedName = request.Name.Trim();
|
||||
if (await tenantRepository.ExistsByNameAsync(normalizedName, excludeTenantId: request.TenantId, cancellationToken))
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.Conflict, $"租户名称 {normalizedName} 已存在");
|
||||
}
|
||||
|
||||
// 4. (空行后) 校验联系人手机号唯一性(仅当填写时)
|
||||
// 4. 校验联系人手机号唯一性(仅当填写时)
|
||||
if (!string.IsNullOrWhiteSpace(request.ContactPhone))
|
||||
{
|
||||
var normalizedPhone = request.ContactPhone.Trim();
|
||||
@@ -51,7 +51,7 @@ public sealed class UpdateTenantCommandHandler(
|
||||
}
|
||||
}
|
||||
|
||||
// 5. (空行后) 更新基础信息(禁止修改 Code)
|
||||
// 5. 更新基础信息(禁止修改 Code)
|
||||
tenant.Name = normalizedName;
|
||||
tenant.ShortName = string.IsNullOrWhiteSpace(request.ShortName) ? null : request.ShortName.Trim();
|
||||
tenant.Industry = string.IsNullOrWhiteSpace(request.Industry) ? null : request.Industry.Trim();
|
||||
@@ -59,11 +59,11 @@ public sealed class UpdateTenantCommandHandler(
|
||||
tenant.ContactPhone = string.IsNullOrWhiteSpace(request.ContactPhone) ? null : request.ContactPhone.Trim();
|
||||
tenant.ContactEmail = string.IsNullOrWhiteSpace(request.ContactEmail) ? null : request.ContactEmail.Trim();
|
||||
|
||||
// 6. (空行后) 持久化更新
|
||||
// 6. 持久化更新
|
||||
await tenantRepository.UpdateTenantAsync(tenant, cancellationToken);
|
||||
await tenantRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 7. (空行后) 记录日志
|
||||
// 7. 记录日志
|
||||
logger.LogInformation("已更新租户基础信息 {TenantId}", tenant.Id);
|
||||
|
||||
return Unit.Value;
|
||||
|
||||
@@ -45,18 +45,18 @@ public sealed class UpdateTenantPackageCommandHandler(ITenantPackageRepository p
|
||||
package.IsPublicVisible = request.IsPublicVisible;
|
||||
package.IsAllowNewTenantPurchase = request.IsAllowNewTenantPurchase;
|
||||
|
||||
// 3. (空行后) 更新发布状态(若未传则保持不变,避免默认值覆盖)
|
||||
// 3. 更新发布状态(若未传则保持不变,避免默认值覆盖)
|
||||
if (request.PublishStatus.HasValue)
|
||||
{
|
||||
package.PublishStatus = request.PublishStatus.Value;
|
||||
}
|
||||
|
||||
// 4. (空行后) 更新展示配置(推荐与标签)
|
||||
// 4. 更新展示配置(推荐与标签)
|
||||
package.IsRecommended = request.IsRecommended;
|
||||
package.Tags = request.Tags ?? [];
|
||||
package.SortOrder = request.SortOrder;
|
||||
|
||||
// 5. (空行后) 持久化并返回
|
||||
// 5. 持久化并返回
|
||||
await packageRepository.UpdateAsync(package, cancellationToken);
|
||||
await packageRepository.SaveChangesAsync(cancellationToken);
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ public sealed class GetTenantQuotaUsageHistoryQueryValidator : AbstractValidator
|
||||
RuleFor(x => x.Page).GreaterThanOrEqualTo(1);
|
||||
RuleFor(x => x.PageSize).InclusiveBetween(1, 100);
|
||||
|
||||
// (空行后) 时间范围校验
|
||||
// 时间范围校验
|
||||
When(x => x.StartDate.HasValue && x.EndDate.HasValue, () =>
|
||||
{
|
||||
RuleFor(x => x.EndDate!.Value).GreaterThanOrEqualTo(x => x.StartDate!.Value);
|
||||
|
||||
Reference in New Issue
Block a user