From e92b333076b167f923a5ca1d3d02bfc1e360834c Mon Sep 17 00:00:00 2001 From: MSuMshk <173331402+msumshk@users.noreply.github.com> Date: Mon, 2 Feb 2026 21:43:22 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=B2=BE=E7=AE=80=E6=89=8B?= =?UTF-8?q?=E5=8A=A8=E5=88=9B=E5=BB=BA=E7=A7=9F=E6=88=B7=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除冗余字段:CoverImageUrl、Website、SuspendedAt、SuspensionReason - 移除冗余字段:ScheduledPackageId、AdditionalDataJson、ReviewedByName、ReviewRemarks、AdminMerchantId - 更新Handler移除对应字段的处理逻辑 Co-Authored-By: Claude Opus 4.5 --- .../Commands/CreateTenantManuallyCommand.cs | 45 ----------- .../CreateTenantManuallyCommandHandler.cs | 74 ++++++------------- 2 files changed, 21 insertions(+), 98 deletions(-) diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Commands/CreateTenantManuallyCommand.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Commands/CreateTenantManuallyCommand.cs index 35c2cf3..106fa01 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Commands/CreateTenantManuallyCommand.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Commands/CreateTenantManuallyCommand.cs @@ -42,16 +42,6 @@ public sealed record CreateTenantManuallyCommand : IRequest /// public string? LogoUrl { get; init; } - /// - /// 封面图地址。 - /// - public string? CoverImageUrl { get; init; } - - /// - /// 官网地址。 - /// - public string? Website { get; init; } - /// /// 国家/地区。 /// @@ -97,16 +87,6 @@ public sealed record CreateTenantManuallyCommand : IRequest /// public string? Remarks { get; init; } - /// - /// 暂停时间。 - /// - public DateTime? SuspendedAt { get; init; } - - /// - /// 暂停原因。 - /// - public string? SuspensionReason { get; init; } - /// /// 租户状态。 /// @@ -153,11 +133,6 @@ public sealed record CreateTenantManuallyCommand : IRequest /// public SubscriptionStatus SubscriptionStatus { get; init; } = SubscriptionStatus.Active; - /// - /// 预约变更的套餐 ID。 - /// - public string? ScheduledPackageId { get; init; } - /// /// 订阅备注。 /// @@ -215,21 +190,6 @@ public sealed record CreateTenantManuallyCommand : IRequest /// public string? BankName { get; init; } - /// - /// 附加数据 JSON。 - /// - public string? AdditionalDataJson { get; init; } - - /// - /// 审核人姓名。 - /// - public string? ReviewedByName { get; init; } - - /// - /// 审核备注。 - /// - public string? ReviewRemarks { get; init; } - // 4. 管理员账号(identity.identity_users) /// @@ -252,11 +212,6 @@ public sealed record CreateTenantManuallyCommand : IRequest /// public string? AdminAvatar { get; init; } - /// - /// 管理员所属商户 ID。 - /// - public string? AdminMerchantId { get; init; } - // 5. 账单信息(public.tenant_billing_statements) /// diff --git a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantManuallyCommandHandler.cs b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantManuallyCommandHandler.cs index 2ed0c7e..a7a0c10 100644 --- a/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantManuallyCommandHandler.cs +++ b/src/Application/TakeoutSaaS.Application/App/Tenants/Handlers/CreateTenantManuallyCommandHandler.cs @@ -54,58 +54,34 @@ public sealed class CreateTenantManuallyCommandHandler( throw new BusinessException(ErrorCodes.BadRequest, "套餐 ID 无效"); } - // 3. 解析可选的预约套餐 ID - long? scheduledPackageId = null; - if (!string.IsNullOrWhiteSpace(request.ScheduledPackageId)) - { - if (!long.TryParse(request.ScheduledPackageId, out var parsedScheduledId) || parsedScheduledId <= 0) - { - throw new BusinessException(ErrorCodes.BadRequest, "预约套餐 ID 无效"); - } - - scheduledPackageId = parsedScheduledId; - } - - // 4. 解析可选的管理员商户 ID - long? adminMerchantId = null; - if (!string.IsNullOrWhiteSpace(request.AdminMerchantId)) - { - if (!long.TryParse(request.AdminMerchantId, out var parsedMerchantId) || parsedMerchantId <= 0) - { - throw new BusinessException(ErrorCodes.BadRequest, "管理员商户 ID 无效"); - } - - adminMerchantId = parsedMerchantId; - } - - // 5. 校验租户编码唯一性 + // 3. 校验租户编码唯一性 if (await tenantRepository.ExistsByCodeAsync(code, null, cancellationToken)) { throw new BusinessException(ErrorCodes.Conflict, "租户编码已存在"); } - // 6. 校验套餐存在性 + // 4. 校验套餐存在性 var package = await tenantPackageRepository.GetByIdAsync(tenantPackageId, cancellationToken); if (package is null) { throw new BusinessException(ErrorCodes.NotFound, "套餐不存在"); } - // 7. 生成租户 ID + // 5. 生成租户 ID var tenantId = idGenerator.NextId(); - // 8. 校验管理员账号唯一性(租户内) + // 6. 校验管理员账号唯一性(租户内) if (await identityUserRepository.ExistsByAccountAsync(PortalType.Tenant, tenantId, adminAccount, null, cancellationToken)) { throw new BusinessException(ErrorCodes.Conflict, "管理员账号已存在"); } - // 9. 计算订阅时间 + // 7. 计算订阅时间 var effectiveFrom = request.SubscriptionEffectiveFrom ?? DateTime.UtcNow; var effectiveTo = effectiveFrom.AddMonths(request.DurationMonths); var nextBillingDate = request.NextBillingDate ?? effectiveTo; - // 10. 创建管理员账号实体 + // 8. 创建管理员账号实体 var adminUser = new IdentityUser { Id = idGenerator.NextId(), @@ -121,23 +97,23 @@ public sealed class CreateTenantManuallyCommandHandler( LockedUntil = null, LastLoginAt = null, MustChangePassword = false, - MerchantId = adminMerchantId, + MerchantId = null, Avatar = request.AdminAvatar?.Trim() }; adminUser.PasswordHash = passwordHasher.HashPassword(adminUser, request.AdminPassword); - // 11. 【Saga 步骤 1】先在 Identity 库创建管理员账号 + // 9. 【Saga 步骤 1】先在 Identity 库创建管理员账号 await identityUserRepository.AddAsync(adminUser, cancellationToken); await identityUserRepository.SaveChangesAsync(cancellationToken); logger.LogInformation("租户 {TenantId} 管理员账号 {AdminUserId} 创建成功", tenantId, adminUser.Id); try { - // 12. 根据是否跳过审核确定租户状态和认证状态 + // 10. 根据是否跳过审核确定租户状态和认证状态 var tenantStatus = request.IsSkipApproval ? TenantStatus.Active : TenantStatus.PendingReview; var verificationStatus = request.IsSkipApproval ? TenantVerificationStatus.Approved : TenantVerificationStatus.Pending; - // 13. 创建租户实体 + // 11. 创建租户实体 var tenant = new Tenant { Id = tenantId, @@ -147,8 +123,6 @@ public sealed class CreateTenantManuallyCommandHandler( LegalEntityName = request.LegalEntityName?.Trim(), Industry = request.Industry?.Trim(), LogoUrl = request.LogoUrl?.Trim(), - CoverImageUrl = request.CoverImageUrl?.Trim(), - Website = request.Website?.Trim(), Country = request.Country?.Trim(), Province = request.Province?.Trim(), City = request.City?.Trim(), @@ -159,14 +133,12 @@ public sealed class CreateTenantManuallyCommandHandler( Tags = request.Tags?.Trim(), Remarks = request.Remarks?.Trim(), Status = tenantStatus, - SuspendedAt = request.SuspendedAt, - SuspensionReason = request.SuspensionReason?.Trim(), EffectiveFrom = effectiveFrom, EffectiveTo = effectiveTo, PrimaryOwnerUserId = adminUser.Id }; - // 14. 创建订阅实体 + // 12. 创建订阅实体 var subscription = new TenantSubscription { Id = idGenerator.NextId(), @@ -177,11 +149,10 @@ public sealed class CreateTenantManuallyCommandHandler( EffectiveTo = effectiveTo, NextBillingDate = nextBillingDate, AutoRenew = request.AutoRenew, - ScheduledPackageId = scheduledPackageId, Notes = request.SubscriptionNotes?.Trim() }; - // 15. 创建认证资料实体 + // 13. 创建认证资料实体 var verification = new TenantVerificationProfile { Id = idGenerator.NextId(), @@ -196,18 +167,15 @@ public sealed class CreateTenantManuallyCommandHandler( BankAccountName = request.BankAccountName?.Trim(), BankAccountNumber = request.BankAccountNumber?.Trim(), BankName = request.BankName?.Trim(), - AdditionalDataJson = request.AdditionalDataJson?.Trim(), SubmittedAt = DateTime.UtcNow, ReviewedAt = request.IsSkipApproval ? DateTime.UtcNow : null, - ReviewedBy = request.IsSkipApproval ? currentUserAccessor.UserId : null, - ReviewedByName = request.IsSkipApproval ? request.ReviewedByName?.Trim() : null, - ReviewRemarks = request.IsSkipApproval ? request.ReviewRemarks?.Trim() : null + ReviewedBy = request.IsSkipApproval ? currentUserAccessor.UserId : null }; - // 16. 根据套餐配额创建配额使用记录 + // 14. 根据套餐配额创建配额使用记录 var quotaUsages = CreateQuotaUsagesFromPackage(tenantId, package); - // 17. 创建账单记录和支付记录(可选) + // 15. 创建账单记录和支付记录(可选) TenantBillingStatement? billing = null; TenantPayment? payment = null; if (request.CreateBilling) @@ -225,20 +193,20 @@ public sealed class CreateTenantManuallyCommandHandler( request.BillingStatus, request.BillingNotes); - // 17. 如果账单状态为已支付,创建支付记录 + // 16. 如果账单状态为已支付,创建支付记录 if (request.BillingStatus == TenantBillingStatus.Paid && billing.AmountDue > 0) { payment = CreatePaymentFromBilling(tenantId, billing); } } - // 18. 【Saga 步骤 2】在 App 库创建租户、订阅、认证资料、配额使用记录、账单、支付记录(使用事务) + // 17. 【Saga 步骤 2】在 App 库创建租户、订阅、认证资料、配额使用记录、账单、支付记录(使用事务) await tenantRepository.CreateTenantWithRelatedDataAsync(tenant, subscription, verification, quotaUsages, billing, payment, cancellationToken); logger.LogInformation("租户 {TenantId} 及相关数据创建成功,跳过审核:{IsSkipApproval}", tenantId, request.IsSkipApproval); } catch (Exception ex) { - // 16. 【Saga 补偿】App 库操作失败,回滚 Identity 库的管理员账号 + // 18. 【Saga 补偿】App 库操作失败,回滚 Identity 库的管理员账号 // 记录完整异常信息(包括内部异常) var fullErrorMessage = ex.InnerException?.Message ?? ex.Message; logger.LogError(ex, "租户 {TenantId} 创建失败,错误详情:{ErrorDetail},开始回滚管理员账号 {AdminUserId}", @@ -252,7 +220,7 @@ public sealed class CreateTenantManuallyCommandHandler( } catch (Exception rollbackEx) { - // 17. 补偿失败,记录严重错误(需要人工介入) + // 19. 补偿失败,记录严重错误(需要人工介入) logger.LogCritical( rollbackEx, "严重:租户 {TenantId} 创建失败且管理员账号 {AdminUserId} 回滚失败,需要人工介入清理", @@ -260,11 +228,11 @@ public sealed class CreateTenantManuallyCommandHandler( adminUser.Id); } - // 18. 重新抛出业务异常(包含详细错误信息) + // 20. 重新抛出业务异常(包含详细错误信息) throw new BusinessException(ErrorCodes.InternalServerError, $"创建租户失败:{fullErrorMessage}"); } - // 19. 查询并返回租户详情 + // 21. 查询并返回租户详情 var detail = await mediator.Send(new GetTenantDetailQuery { TenantId = tenantId }, cancellationToken); return detail ?? throw new BusinessException(ErrorCodes.InternalServerError, "创建租户成功但查询详情失败"); }